Merge lp:~rvb/maas/bug-1384001-redux-1.7 into lp:maas/1.7

Proposed by Raphaël Badin
Status: Merged
Approved by: Raphaël Badin
Approved revision: no longer in the source branch.
Merged at revision: 3283
Proposed branch: lp:~rvb/maas/bug-1384001-redux-1.7
Merge into: lp:maas/1.7
Diff against target: 101 lines (+45/-4)
2 files modified
src/maasserver/middleware.py (+16/-2)
src/maasserver/tests/test_middleware.py (+29/-2)
To merge this branch: bzr merge lp:~rvb/maas/bug-1384001-redux-1.7
Reviewer Review Type Date Requested Status
Raphaël Badin (community) Approve
Review via email: mp+239914@code.launchpad.net

Commit message

Backport 3305: deal with the first exception contained in a MultipleFailure error in the RPCAPIMiddleware if the exceptions contains only one error.

Backport 3304: Return 503 response for PowerActionAlreadyInProgress exceptions. Add 'Retry-after' header for 503 responses: this will allow clients to retry failed requests.

To post a comment you must log in.
Revision history for this message
Raphaël Badin (rvb) wrote :

Simple backport, self-approving.

review: Approve
Revision history for this message
MAAS Lander (maas-lander) wrote :
Download full text (19.5 KiB)

The attempt to merge lp:~rvb/maas/bug-1384001-redux-1.7 into lp:maas/1.7 failed. Below is the output from the failed tests.

Ign http://security.ubuntu.com trusty-security InRelease
Ign http://nova.clouds.archive.ubuntu.com trusty InRelease
Get:1 http://security.ubuntu.com trusty-security Release.gpg [933 B]
Ign http://nova.clouds.archive.ubuntu.com trusty-updates InRelease
Get:2 http://security.ubuntu.com trusty-security Release [59.7 kB]
Hit http://nova.clouds.archive.ubuntu.com trusty Release.gpg
Get:3 http://nova.clouds.archive.ubuntu.com trusty-updates Release.gpg [933 B]
Hit http://nova.clouds.archive.ubuntu.com trusty Release
Get:4 http://nova.clouds.archive.ubuntu.com trusty-updates Release [59.7 kB]
Get:5 http://security.ubuntu.com trusty-security/main Sources [48.2 kB]
Hit http://nova.clouds.archive.ubuntu.com trusty/main Sources
Get:6 http://security.ubuntu.com trusty-security/universe Sources [11.2 kB]
Get:7 http://security.ubuntu.com trusty-security/main amd64 Packages [149 kB]
Get:8 http://security.ubuntu.com trusty-security/universe amd64 Packages [50.4 kB]
Hit http://nova.clouds.archive.ubuntu.com trusty/universe Sources
Hit http://nova.clouds.archive.ubuntu.com trusty/main amd64 Packages
Hit http://nova.clouds.archive.ubuntu.com trusty/universe amd64 Packages
Hit http://security.ubuntu.com trusty-security/main Translation-en
Hit http://nova.clouds.archive.ubuntu.com trusty/main Translation-en
Hit http://nova.clouds.archive.ubuntu.com trusty/universe Translation-en
Hit http://security.ubuntu.com trusty-security/universe Translation-en
Get:9 http://nova.clouds.archive.ubuntu.com trusty-updates/main Sources [133 kB]
Get:10 http://nova.clouds.archive.ubuntu.com trusty-updates/universe Sources [89.3 kB]
Get:11 http://nova.clouds.archive.ubuntu.com trusty-updates/main amd64 Packages [349 kB]
Get:12 http://nova.clouds.archive.ubuntu.com trusty-updates/universe amd64 Packages [216 kB]
Hit http://nova.clouds.archive.ubuntu.com trusty-updates/main Translation-en
Hit http://nova.clouds.archive.ubuntu.com trusty-updates/universe Translation-en
Ign http://nova.clouds.archive.ubuntu.com trusty/main Translation-en_US
Ign http://nova.clouds.archive.ubuntu.com trusty/universe Translation-en_US
Fetched 1,167 kB in 2s (409 kB/s)
Reading package lists...
sudo DEBIAN_FRONTEND=noninteractive apt-get -y \
     --no-install-recommends install apache2 authbind bind9 bind9utils build-essential bzr-builddeb curl daemontools debhelper dh-apport distro-info dnsutils firefox freeipmi-tools gjs ipython isc-dhcp-common libjs-raphael libjs-yui3-full libjs-yui3-min libpq-dev make pep8 postgresql pyflakes python-amqplib python-bzrlib python-celery python-convoy python-crochet python-cssselect python-curtin python-dev python-distro-info python-django python-django-piston python-django-south python-djorm-ext-pgarray python-docutils python-extras python-fixtures python-flake8 python-formencode python-hivex python-httplib2 python-jinja2 python-jsonschema python-lockfile python-lxml python-mimeparse python-mock python-netaddr python-netifaces python-nose python-oauth python-oops python-oops-amqp python-oops-datedir-repo python-oops-twisted python-oops-wsgi python-o...

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-10 11:03:50 +0000
+++ src/maasserver/middleware.py 2014-10-28 21:30:33 +0000
@@ -350,11 +350,15 @@
350350
351 handled_exceptions = {351 handled_exceptions = {
352 NoConnectionsAvailable: httplib.SERVICE_UNAVAILABLE,352 NoConnectionsAvailable: httplib.SERVICE_UNAVAILABLE,
353 PowerActionAlreadyInProgress: httplib.CONFLICT,353 PowerActionAlreadyInProgress: httplib.SERVICE_UNAVAILABLE,
354 MultipleFailures: httplib.INTERNAL_SERVER_ERROR,354 MultipleFailures: httplib.INTERNAL_SERVER_ERROR,
355 TimeoutError: httplib.GATEWAY_TIMEOUT,355 TimeoutError: httplib.GATEWAY_TIMEOUT,
356 }356 }
357357
358 # Default 'Retry-After' header sent for httplib.SERVICE_UNAVAILABLE
359 # responses.
360 RETRY_AFTER_SERVICE_UNAVAILABLE = 10
361
358 def process_exception(self, request, exception):362 def process_exception(self, request, exception):
359 path_matcher = re.compile(settings.API_URL_REGEXP)363 path_matcher = re.compile(settings.API_URL_REGEXP)
360 if not path_matcher.match(get_relative_path(request.path)):364 if not path_matcher.match(get_relative_path(request.path)):
@@ -370,6 +374,12 @@
370374
371 status = self.handled_exceptions[exception.__class__]375 status = self.handled_exceptions[exception.__class__]
372 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)
373 for failure in exception.args:383 for failure in exception.args:
374 logging.exception(exception)384 logging.exception(exception)
375 error_message = "\n".join(385 error_message = "\n".join(
@@ -380,6 +390,10 @@
380 error_message = get_error_message_for_exception(exception)390 error_message = get_error_message_for_exception(exception)
381391
382 encoding = b'utf-8'392 encoding = b'utf-8'
383 return HttpResponse(393 response = HttpResponse(
384 content=error_message.encode(encoding), status=status,394 content=error_message.encode(encoding), status=status,
385 mimetype=b"text/plain; charset=%s" % encoding)395 mimetype=b"text/plain; charset=%s" % encoding)
396 if status == httplib.SERVICE_UNAVAILABLE:
397 response['Retry-After'] = (
398 self.RETRY_AFTER_SERVICE_UNAVAILABLE)
399 return response
386400
=== modified file 'src/maasserver/tests/test_middleware.py'
--- src/maasserver/tests/test_middleware.py 2014-10-10 13:25:21 +0000
+++ src/maasserver/tests/test_middleware.py 2014-10-28 21:30:33 +0000
@@ -446,7 +446,21 @@
446 (httplib.SERVICE_UNAVAILABLE, error_message),446 (httplib.SERVICE_UNAVAILABLE, error_message),
447 (response.status_code, response.content))447 (response.status_code, response.content))
448448
449 def test_power_action_already_in_progress_returned_as_409(self):449 def test_503_response_includes_retry_after_header_by_default(self):
450 middleware = APIRPCErrorsMiddleware()
451 request = factory.make_fake_request(
452 "/api/1.0/" + factory.make_string(), 'POST')
453 error = NoConnectionsAvailable(factory.make_name())
454 response = middleware.process_exception(request, error)
455
456 self.assertEqual(
457 (
458 httplib.SERVICE_UNAVAILABLE,
459 '%s' % middleware.RETRY_AFTER_SERVICE_UNAVAILABLE,
460 ),
461 (response.status_code, response['Retry-after']))
462
463 def test_power_action_already_in_progress_returned_as_503(self):
450 middleware = APIRPCErrorsMiddleware()464 middleware = APIRPCErrorsMiddleware()
451 request = factory.make_fake_request(465 request = factory.make_fake_request(
452 "/api/1.0/" + factory.make_string(), 'POST')466 "/api/1.0/" + factory.make_string(), 'POST')
@@ -457,7 +471,7 @@
457 response = middleware.process_exception(request, error)471 response = middleware.process_exception(request, error)
458472
459 self.assertEqual(473 self.assertEqual(
460 (httplib.CONFLICT, error_message),474 (httplib.SERVICE_UNAVAILABLE, error_message),
461 (response.status_code, response.content))475 (response.status_code, response.content))
462476
463 def test_multiple_failures_returned_as_500(self):477 def test_multiple_failures_returned_as_500(self):
@@ -480,6 +494,19 @@
480 (httplib.INTERNAL_SERVER_ERROR, expected_error_message),494 (httplib.INTERNAL_SERVER_ERROR, expected_error_message),
481 (response.status_code, response.content))495 (response.status_code, response.content))
482496
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
483 def test_handles_TimeoutError(self):510 def test_handles_TimeoutError(self):
484 middleware = APIRPCErrorsMiddleware()511 middleware = APIRPCErrorsMiddleware()
485 request = factory.make_fake_request(512 request = factory.make_fake_request(

Subscribers

People subscribed via source and target branches

to all changes: