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

Proposed by Raphaël Badin on 2014-10-28
Status: Merged
Approved by: Raphaël Badin on 2014-10-29
Approved revision: 3284
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 on 2014-10-28
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.
Raphaël Badin (rvb) wrote :

Simple backport, self-approving.

review: Approve
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
1=== modified file 'src/maasserver/middleware.py'
2--- src/maasserver/middleware.py 2014-10-10 11:03:50 +0000
3+++ src/maasserver/middleware.py 2014-10-28 21:30:33 +0000
4@@ -350,11 +350,15 @@
5
6 handled_exceptions = {
7 NoConnectionsAvailable: httplib.SERVICE_UNAVAILABLE,
8- PowerActionAlreadyInProgress: httplib.CONFLICT,
9+ PowerActionAlreadyInProgress: httplib.SERVICE_UNAVAILABLE,
10 MultipleFailures: httplib.INTERNAL_SERVER_ERROR,
11 TimeoutError: httplib.GATEWAY_TIMEOUT,
12 }
13
14+ # Default 'Retry-After' header sent for httplib.SERVICE_UNAVAILABLE
15+ # responses.
16+ RETRY_AFTER_SERVICE_UNAVAILABLE = 10
17+
18 def process_exception(self, request, exception):
19 path_matcher = re.compile(settings.API_URL_REGEXP)
20 if not path_matcher.match(get_relative_path(request.path)):
21@@ -370,6 +374,12 @@
22
23 status = self.handled_exceptions[exception.__class__]
24 if isinstance(exception, MultipleFailures):
25+ # If only one exception has been raised, process this exception:
26+ # this allows MAAS to convert this exception into the proper
27+ # type of response (e.g. 503) instead of the 500 response that
28+ # MultipleFailures is transformed into.
29+ if len(exception.args) == 1:
30+ return self.process_exception(request, exception.args[0].value)
31 for failure in exception.args:
32 logging.exception(exception)
33 error_message = "\n".join(
34@@ -380,6 +390,10 @@
35 error_message = get_error_message_for_exception(exception)
36
37 encoding = b'utf-8'
38- return HttpResponse(
39+ response = HttpResponse(
40 content=error_message.encode(encoding), status=status,
41 mimetype=b"text/plain; charset=%s" % encoding)
42+ if status == httplib.SERVICE_UNAVAILABLE:
43+ response['Retry-After'] = (
44+ self.RETRY_AFTER_SERVICE_UNAVAILABLE)
45+ return response
46
47=== modified file 'src/maasserver/tests/test_middleware.py'
48--- src/maasserver/tests/test_middleware.py 2014-10-10 13:25:21 +0000
49+++ src/maasserver/tests/test_middleware.py 2014-10-28 21:30:33 +0000
50@@ -446,7 +446,21 @@
51 (httplib.SERVICE_UNAVAILABLE, error_message),
52 (response.status_code, response.content))
53
54- def test_power_action_already_in_progress_returned_as_409(self):
55+ def test_503_response_includes_retry_after_header_by_default(self):
56+ middleware = APIRPCErrorsMiddleware()
57+ request = factory.make_fake_request(
58+ "/api/1.0/" + factory.make_string(), 'POST')
59+ error = NoConnectionsAvailable(factory.make_name())
60+ response = middleware.process_exception(request, error)
61+
62+ self.assertEqual(
63+ (
64+ httplib.SERVICE_UNAVAILABLE,
65+ '%s' % middleware.RETRY_AFTER_SERVICE_UNAVAILABLE,
66+ ),
67+ (response.status_code, response['Retry-after']))
68+
69+ def test_power_action_already_in_progress_returned_as_503(self):
70 middleware = APIRPCErrorsMiddleware()
71 request = factory.make_fake_request(
72 "/api/1.0/" + factory.make_string(), 'POST')
73@@ -457,7 +471,7 @@
74 response = middleware.process_exception(request, error)
75
76 self.assertEqual(
77- (httplib.CONFLICT, error_message),
78+ (httplib.SERVICE_UNAVAILABLE, error_message),
79 (response.status_code, response.content))
80
81 def test_multiple_failures_returned_as_500(self):
82@@ -480,6 +494,19 @@
83 (httplib.INTERNAL_SERVER_ERROR, expected_error_message),
84 (response.status_code, response.content))
85
86+ def test_multiple_failures_with_one_exception(self):
87+ middleware = APIRPCErrorsMiddleware()
88+ request = factory.make_fake_request(
89+ "/api/1.0/" + factory.make_string(), 'POST')
90+ expected_error_message = factory.make_name("error")
91+ unique_exception = PowerActionAlreadyInProgress(expected_error_message)
92+ exception = MultipleFailures(Failure(unique_exception))
93+ response = middleware.process_exception(request, exception)
94+
95+ self.assertEqual(
96+ (httplib.SERVICE_UNAVAILABLE, expected_error_message),
97+ (response.status_code, response.content))
98+
99 def test_handles_TimeoutError(self):
100 middleware = APIRPCErrorsMiddleware()
101 request = factory.make_fake_request(

Subscribers

People subscribed via source and target branches

to all changes: