Merge lp:~facundo/ubuntu-rest-scopes/ignore-network-server-crashes into lp:ubuntu-rest-scopes

Proposed by Facundo Batista
Status: Merged
Approved by: Facundo Batista
Approved revision: 538
Merged at revision: 538
Proposed branch: lp:~facundo/ubuntu-rest-scopes/ignore-network-server-crashes
Merge into: lp:ubuntu-rest-scopes
Diff against target: 138 lines (+81/-5)
2 files modified
src/rest_scopes.py (+20/-2)
src/tests/test_rest_scopes.py (+61/-3)
To merge this branch: bzr merge lp:~facundo/ubuntu-rest-scopes/ignore-network-server-crashes
Reviewer Review Type Date Requested Status
Guillermo Gonzalez Approve
Review via email: mp+324130@code.launchpad.net

Commit message

Ignore network and server errors when hitting backends.

Description of the change

Ignore network and server errors when hitting backends.

This code is copied from curucu, where we ended up doing the same thing.

Note that logging and informing metrics is not done in that code, as is done already when hitting the network...

To post a comment you must log in.
Revision history for this message
Guillermo Gonzalez (verterok) wrote :

LGTM

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/rest_scopes.py'
2--- src/rest_scopes.py 2016-03-14 06:20:14 +0000
3+++ src/rest_scopes.py 2017-05-16 19:13:32 +0000
4@@ -1,9 +1,10 @@
5-# Copyright 2014-2015 Canonical
6+# Copyright 2014-2017 Canonical
7 # All Rights Reserved
8
9 """Rest Scopes server."""
10
11 import collections
12+import httplib
13 import importlib
14 import json
15 import logging
16@@ -299,7 +300,24 @@
17
18 # fix environment and serve
19 environ["PATH_INFO"] = scope_path
20- return scope(environ, start_response)
21+ try:
22+ return scope(environ, start_response)
23+ except urllib2.HTTPError as exc:
24+ if exc.code < 500:
25+ raise
26+ error_message = str(exc)
27+ except urllib2.URLError as exc:
28+ error_message = "URLError: " + str(exc.reason)
29+ except httplib.HTTPException as exc:
30+ error_message = "HTTPException: " + str(exc)
31+
32+ # the backend didn't behave
33+ msg = "Original error: " + error_message
34+ headers = {
35+ 'Content-Type': 'text/plain',
36+ 'Content-Length': str(len(msg)),
37+ }
38+ return self._error("502 BAD GATEWAY", start_response, msg, **headers)
39
40 def curate_list(self, country_blacklist, scopes):
41 """Remove blacklisted scopes."""
42
43=== modified file 'src/tests/test_rest_scopes.py'
44--- src/tests/test_rest_scopes.py 2016-02-11 13:16:24 +0000
45+++ src/tests/test_rest_scopes.py 2017-05-16 19:13:32 +0000
46@@ -1,8 +1,9 @@
47-# Copyright 2014-2015 Canonical
48+# Copyright 2014-2017 Canonical
49 # All Rights Reserved
50
51 """Tests for the Rest Scopes server."""
52
53+import httplib
54 import io
55 import json
56 import uuid
57@@ -211,6 +212,63 @@
58 }
59 app_mock.assert_called_with(new_environ, 'start_response')
60
61+ def test_exception_problem(self):
62+ app = rest_scopes.App()
63+ app.scope_apps['somescope'] = app_mock = Mock()
64+ environ = {'PATH_INFO': '/somescope/somemethod'}
65+
66+ # should transmit the problem
67+ app_mock.side_effect = ValueError("scope crashed")
68+ self.assertRaises(ValueError, app, environ, 'start_response')
69+
70+ def test_exception_httperror_problem(self):
71+ app = rest_scopes.App()
72+ app.scope_apps['somescope'] = app_mock = Mock()
73+ environ = {'PATH_INFO': '/somescope/somemethod'}
74+
75+ # should transmit the problem
76+ app_mock.side_effect = urllib2.HTTPError(None, 404, "Not Found", {}, None)
77+ self.assertRaises(urllib2.HTTPError, app, environ, 'start_response')
78+
79+ def test_exception_httperror_ignore(self):
80+ app = rest_scopes.App()
81+ app.scope_apps['somescope'] = app_mock = Mock()
82+ environ = {'PATH_INFO': '/somescope/somemethod'}
83+
84+ # ignore server side errors, should return 502
85+ app_mock.side_effect = urllib2.HTTPError(None, 500, "Internal Server Error", {}, None)
86+ start_response = Mock()
87+ resp = app(environ, start_response)
88+ msg = json.loads(resp[0])['message']
89+ self.assertEqual(msg, "Original error: HTTP Error 500: Internal Server Error")
90+ start_response.assert_called_with('502 BAD GATEWAY', ANY)
91+
92+ def test_exception_urlerror_ignore(self):
93+ app = rest_scopes.App()
94+ app.scope_apps['somescope'] = app_mock = Mock()
95+ environ = {'PATH_INFO': '/somescope/somemethod'}
96+
97+ # ignore network errors, should return 502
98+ app_mock.side_effect = urllib2.URLError("test problem")
99+ start_response = Mock()
100+ resp = app(environ, start_response)
101+ msg = json.loads(resp[0])['message']
102+ self.assertEqual(msg, "Original error: URLError: test problem")
103+ start_response.assert_called_with('502 BAD GATEWAY', ANY)
104+
105+ def test_exception_httpexception_ignore(self):
106+ app = rest_scopes.App()
107+ app.scope_apps['somescope'] = app_mock = Mock()
108+ environ = {'PATH_INFO': '/somescope/somemethod'}
109+
110+ # ignore network errors, should return 502
111+ app_mock.side_effect = httplib.HTTPException("test problem")
112+ start_response = Mock()
113+ resp = app(environ, start_response)
114+ msg = json.loads(resp[0])['message']
115+ self.assertEqual(msg, "Original error: HTTPException: test problem")
116+ start_response.assert_called_with('502 BAD GATEWAY', ANY)
117+
118
119 class ListRemoteScopesTestCase(TestCase):
120 """Test the remote scopes listing."""
121@@ -409,7 +467,7 @@
122 'name': 'name',
123 'description': 'desc',
124 'author': 'John Doe',
125- 'base_url': "http://localhost:8000/somescope",
126+ 'base_url': "http://localhost:8000/somescope",
127 'invisible': False,
128 'icon': None,
129 'art': None,
130@@ -514,7 +572,7 @@
131 'name': 'name',
132 'description': 'desc',
133 'author': 'John Doe',
134- 'base_url': "http://localhost:8000/somescope",
135+ 'base_url': "http://localhost:8000/somescope",
136 'invisible': False,
137 'icon': None,
138 'art': None,

Subscribers

People subscribed via source and target branches