Merge lp:~cjohnston/uci-engine/cli-requests into lp:uci-engine
- cli-requests
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Chris Johnston |
Approved revision: | 502 |
Merged at revision: | 518 |
Proposed branch: | lp:~cjohnston/uci-engine/cli-requests |
Merge into: | lp:uci-engine |
Diff against target: |
782 lines (+202/-160) 11 files modified
cli/ci_cli/image.py (+5/-4) cli/ci_cli/status.py (+5/-4) cli/ci_cli/tests/__init__.py (+43/-0) cli/ci_cli/tests/test_cli.py (+56/-44) cli/ci_cli/tests/test_get_ticket_status.py (+31/-31) cli/ci_cli/tests/test_image.py (+22/-38) cli/ci_cli/tests/test_utils.py (+13/-12) cli/ci_cli/utils.py (+9/-14) cli/setup.py (+1/-0) cli/ubuntu-ci (+16/-13) debian/control (+1/-0) |
To merge this branch: | bzr merge lp:~cjohnston/uci-engine/cli-requests |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot (community) | continuous-integration | Approve | |
Andy Doan (community) | Approve | ||
Chris Johnston (community) | Needs Resubmitting | ||
Review via email: mp+220960@code.launchpad.net |
Commit message
Switch the CLI to use python-requests
Description of the change
Swiftclient is already using python-requests, and I think that using python-requests will make working with the gatekeeper easier so lets switch to using it for the CLI.
PS Jenkins bot (ps-jenkins) wrote : | # |
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:499
http://
Executed test runs:
Click here to trigger a rebuild:
http://
Andy Doan (doanac) wrote : | # |
nothing major, but 3 comments
- 500. By Chris Johnston
-
Remove double code block for exceptions
- 501. By Chris Johnston
-
Update test to call ConnectionError on socket.error
- 502. By Chris Johnston
-
More cleanup
Chris Johnston (cjohnston) : | # |
Andy Doan (doanac) : | # |
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:502
http://
Executed test runs:
Click here to trigger a rebuild:
http://
Preview Diff
1 | === modified file 'cli/ci_cli/image.py' |
2 | --- cli/ci_cli/image.py 2014-05-07 14:56:11 +0000 |
3 | +++ cli/ci_cli/image.py 2014-05-27 09:44:30 +0000 |
4 | @@ -15,7 +15,8 @@ |
5 | |
6 | import os |
7 | import sys |
8 | -import urllib2 |
9 | + |
10 | +from requests import exceptions |
11 | |
12 | from ci_cli import utils |
13 | from ci_utils import ( |
14 | @@ -41,11 +42,11 @@ |
15 | url = artifact_base + '?type__exact=IMAGE&ticket__exact={}'.format( |
16 | ticket) |
17 | return utils.get(url) |
18 | - except urllib2.HTTPError, exc: |
19 | - if exc.code == 404 and ticket: |
20 | + except exceptions.HTTPError, exc: |
21 | + if exc.response.status_code == 404 and ticket: |
22 | sys.exit("Ticket number {} not found.".format(ticket)) |
23 | raise |
24 | - except urllib2.URLError, exc: |
25 | + except exceptions.ConnectionError, exc: |
26 | raise |
27 | |
28 | |
29 | |
30 | === modified file 'cli/ci_cli/status.py' |
31 | --- cli/ci_cli/status.py 2014-05-07 14:56:11 +0000 |
32 | +++ cli/ci_cli/status.py 2014-05-27 09:44:30 +0000 |
33 | @@ -14,7 +14,8 @@ |
34 | # with this program. If not, see <http://www.gnu.org/licenses/>. |
35 | |
36 | import sys |
37 | -import urllib2 |
38 | + |
39 | +from requests import exceptions |
40 | |
41 | from ci_cli import utils |
42 | |
43 | @@ -27,11 +28,11 @@ |
44 | else: |
45 | url = utils.CI_URL + utils.TICKET_BASE |
46 | data = utils.get(url) |
47 | - except urllib2.HTTPError, exc: |
48 | - if exc.code == 404 and args.ticket: |
49 | + except exceptions.HTTPError, exc: |
50 | + if exc.response.status_code == 404 and args.ticket: |
51 | sys.exit("Ticket number {} not found.".format(args.ticket)) |
52 | raise |
53 | - except urllib2.URLError, exc: |
54 | + except exceptions.ConnectionError, exc: |
55 | raise |
56 | |
57 | if args.ticket: |
58 | |
59 | === modified file 'cli/ci_cli/tests/__init__.py' |
60 | --- cli/ci_cli/tests/__init__.py 2014-05-07 17:57:12 +0000 |
61 | +++ cli/ci_cli/tests/__init__.py 2014-05-27 09:44:30 +0000 |
62 | @@ -17,10 +17,13 @@ |
63 | import imp |
64 | import os |
65 | import sys |
66 | +import json |
67 | |
68 | from cStringIO import StringIO |
69 | from contextlib import contextmanager |
70 | |
71 | +from requests import exceptions |
72 | + |
73 | from ci_cli import utils |
74 | from ci_utils.testing import TestCaseWithGnupg |
75 | |
76 | @@ -54,6 +57,46 @@ |
77 | utils.CI_URL = utils.AUTH_CONFIG['ci_url'] |
78 | |
79 | |
80 | +class FakeRequestsReturn: |
81 | + """Object to mock the return of python-requests return.""" |
82 | + |
83 | + def __init__(self, code, content, reason=None): |
84 | + self.status_code = code |
85 | + self.content = content |
86 | + self.reason = reason |
87 | + |
88 | + def raise_for_status(self): |
89 | + if 400 == self.status_code: |
90 | + error = '%s Client Error: %s' % (self.status_code, self.reason) |
91 | + e = exceptions.ConnectionError(error) |
92 | + raise e |
93 | + elif 400 < self.status_code < 500: |
94 | + error = '%s Client Error: %s' % (self.status_code, self.reason) |
95 | + e = exceptions.HTTPError(error, response=self) |
96 | + raise e |
97 | + elif 500 <= self.status_code < 600: |
98 | + error = '%s Server Error: %s' % (self.status_code, self.reason) |
99 | + e = exceptions.HTTPError(error, response=self) |
100 | + raise e |
101 | + else: |
102 | + pass |
103 | + |
104 | + def __repr__(self): |
105 | + return self.content |
106 | + |
107 | + def __iter__(self): |
108 | + return (line for line in self.content.split("\n")) |
109 | + |
110 | + def read(self): |
111 | + return self.content |
112 | + |
113 | + def json(self): |
114 | + return json.loads(self.content) |
115 | + |
116 | + def _status_code(self): |
117 | + pass |
118 | + |
119 | + |
120 | class MainScriptTestCase(TestCaseWithGnupg): |
121 | """Load the 'ubuntu-ci' script module for tests. |
122 | |
123 | |
124 | === modified file 'cli/ci_cli/tests/test_cli.py' |
125 | --- cli/ci_cli/tests/test_cli.py 2014-05-12 15:49:28 +0000 |
126 | +++ cli/ci_cli/tests/test_cli.py 2014-05-27 09:44:30 +0000 |
127 | @@ -21,7 +21,6 @@ |
128 | import mock |
129 | import socket |
130 | from testfixtures import LogCapture |
131 | -import urllib2 |
132 | |
133 | from ci_cli import utils |
134 | from ci_cli.tests import ( |
135 | @@ -29,6 +28,7 @@ |
136 | capture_stdout, |
137 | mock_load_config, |
138 | MainScriptTestCase, |
139 | + FakeRequestsReturn, |
140 | ) |
141 | from ci_cli.ticket import new_ticket |
142 | from ci_utils.testing import get_test_file_path |
143 | @@ -41,9 +41,10 @@ |
144 | class CLICommandsTestCase(MainScriptTestCase): |
145 | """Tests cli basic commands.""" |
146 | |
147 | - @mock.patch('urllib2.urlopen') |
148 | + @mock.patch('requests.get') |
149 | @mock.patch('ci_cli.utils.load_config', side_effect=mock_load_config()) |
150 | - def test_ticket_status_one_ticket(self, mock_load_config, mock_urlopen): |
151 | + def test_ticket_status_one_ticket(self, mock_load_config, |
152 | + mock_requests): |
153 | """Verify cli status command when providing ticket number.""" |
154 | data = { |
155 | "current_workflow_step": str(TicketWorkflowStep.IMAGE_BUILDING), |
156 | @@ -58,9 +59,9 @@ |
157 | "added_binaries": "mynewpackage", |
158 | "removed_binaries": "myoldpackage", |
159 | } |
160 | - resp = mock.Mock() |
161 | - resp.read.return_value = json.dumps(data) |
162 | - mock_urlopen.return_value = resp |
163 | + mock_requests.side_effect = [ |
164 | + FakeRequestsReturn(200, json.dumps(data)) |
165 | + ] |
166 | args = ['status', '-t', '5'] |
167 | with capture_stdout(self.cli.main, args) as cm: |
168 | self.assertEquals( |
169 | @@ -76,11 +77,11 @@ |
170 | 'Removed binaries: myoldpackage\n'.format( |
171 | str(TicketWorkflowStep.IMAGE_BUILDING), |
172 | str(TicketWorkflowStepStatus.INPROGRESS))) |
173 | - mock_urlopen.assert_called_once() |
174 | + mock_requests.assert_called_once() |
175 | |
176 | - @mock.patch('urllib2.urlopen') |
177 | + @mock.patch('requests.get') |
178 | @mock.patch('ci_cli.utils.load_config', side_effect=mock_load_config()) |
179 | - def test_ticket_status_all_tickets(self, mock_load_config, mock_urlopen): |
180 | + def test_ticket_status_all_tickets(self, mock_load_config, mock_requests): |
181 | """Verify cli status command for multiple tickets.""" |
182 | data = { |
183 | "objects": [ |
184 | @@ -98,16 +99,16 @@ |
185 | }, |
186 | ] |
187 | } |
188 | - resp = mock.Mock() |
189 | - resp.read.return_value = json.dumps(data) |
190 | - mock_urlopen.return_value = resp |
191 | + mock_requests.side_effect = [ |
192 | + FakeRequestsReturn(200, json.dumps(data)) |
193 | + ] |
194 | args = ['status'] |
195 | with capture_stdout(self.cli.main, args) as cm: |
196 | self.assertEquals( |
197 | cm, |
198 | "Ticket #5, My cool feature is 100 110\n" + |
199 | "Ticket #6, Ubuntu bug fix is 100 110\n") |
200 | - mock_urlopen.assert_called_once() |
201 | + mock_requests.assert_called_once() |
202 | |
203 | @mock.patch('ci_cli.ticket.SubTicket._process') |
204 | @mock.patch('ci_cli.utils.post', |
205 | @@ -154,83 +155,94 @@ |
206 | self.assertTrue("foobar" in new_ticket_.subtickets) |
207 | self.assertTrue("barfoo" in new_ticket_.subtickets) |
208 | |
209 | - @mock.patch('urllib2.urlopen') |
210 | + @mock.patch('requests.get') |
211 | @mock.patch('ci_cli.utils.load_config', side_effect=mock_load_config()) |
212 | - def test_ts_server_returns_urlerror(self, mock_load_config, mock_urlopen): |
213 | + def test_ts_server_returns_urlerror(self, mock_load_config, mock_requests): |
214 | """Test cli response to ts server refusing connection (111).""" |
215 | - mock_urlopen.side_effect = urllib2.URLError(reason=socket.error(111, |
216 | - "Connection refused")) |
217 | + mock_requests.side_effect = [ |
218 | + FakeRequestsReturn( |
219 | + 400, '', socket.error(111, "Connection refused")) |
220 | + ] |
221 | args = ['status'] |
222 | with LogCapture() as lc: |
223 | logger = logging.getLogger() |
224 | self.cli.main(args, log=logger) |
225 | - mock_urlopen.assert_called_once() |
226 | + mock_requests.assert_called_once() |
227 | self.assertTrue("Cannot reach the server at %s." % utils.CI_URL in |
228 | + lc.records[0].msg, lc.records[0].msg) |
229 | + self.assertTrue("Connection refused" in lc.records[0].msg, |
230 | lc.records[0].msg) |
231 | - self.assertTrue("Connection refused" in lc.records[0].msg) |
232 | |
233 | - @mock.patch('urllib2.urlopen') |
234 | + @mock.patch('requests.get') |
235 | @mock.patch('ci_cli.utils.load_config', side_effect=mock_load_config()) |
236 | def test_ts_server_returns_internal_server_error(self, mock_load_config, |
237 | - mock_urlopen): |
238 | + mock_requests): |
239 | """Test cli response to ts server returning error 500.""" |
240 | - mock_urlopen.side_effect = urllib2.HTTPError(None, 500, |
241 | - "INTERNAL SERVER ERROR", |
242 | - None, None) |
243 | + mock_requests.side_effect = [ |
244 | + FakeRequestsReturn( |
245 | + 500, "", "INTERNAL SERVER ERROR" |
246 | + ) |
247 | + ] |
248 | args = ['status'] |
249 | with LogCapture() as lc: |
250 | logger = logging.getLogger() |
251 | self.cli.main(args, log=logger) |
252 | - mock_urlopen.assert_called_once() |
253 | + mock_requests.assert_called_once() |
254 | self.assertEquals("Server at {} reported internal error. Maybe a " |
255 | "database hiccup?".format(utils.CI_URL), |
256 | lc.records[0].msg) |
257 | |
258 | - @mock.patch('urllib2.urlopen') |
259 | + @mock.patch('requests.get') |
260 | @mock.patch('ci_cli.utils.load_config', side_effect=mock_load_config()) |
261 | def test_ts_server_returns_url_not_found(self, mock_load_config, |
262 | - mock_urlopen): |
263 | + mock_requests): |
264 | """Test cli response to ts server returning 404 error.""" |
265 | - mock_urlopen.side_effect = urllib2.HTTPError(None, 404, |
266 | - "Server Not Found", |
267 | - None, None) |
268 | + mock_requests.side_effect = [ |
269 | + FakeRequestsReturn(404, '', 'Not Found') |
270 | + ] |
271 | args = ['status'] |
272 | with LogCapture() as lc: |
273 | logger = logging.getLogger() |
274 | self.cli.main(args, log=logger) |
275 | - mock_urlopen.assert_called_once() |
276 | - self.assertEquals("Server at {} not found. Is 'ci_url' correctly " |
277 | - "set? (HTTP Error 404: Server Not Found)".format( |
278 | - utils.CI_URL), lc.records[0].msg) |
279 | + mock_requests.assert_called_once() |
280 | + self.assertEquals("Server at {} not found. Is 'ci_url' correctly " |
281 | + "set? (404 Client Error: Not Found)".format( |
282 | + utils.CI_URL), lc.records[0].msg) |
283 | |
284 | - @mock.patch('urllib2.urlopen') |
285 | + @mock.patch('requests.get') |
286 | @mock.patch('ci_cli.utils.load_config', side_effect=mock_load_config()) |
287 | def test_ts_server_returns_bad_status_line(self, mock_load_config, |
288 | - mock_urlopen): |
289 | + mock_requests): |
290 | """Test cli response to ts server returning BadStatusLine.""" |
291 | args = ['status'] |
292 | - mock_urlopen.side_effect = httplib.BadStatusLine('') |
293 | + mock_requests.side_effect = httplib.BadStatusLine('') |
294 | with LogCapture() as lc: |
295 | logger = logging.getLogger() |
296 | self.cli.main(args, log=logger) |
297 | - mock_urlopen.assert_called_once() |
298 | + mock_requests.assert_called_once() |
299 | self.assertEquals("Server at {} replied with an empty response. Is " |
300 | "'ci_url' pointing to the correct service?".format( |
301 | utils.CI_URL), lc.records[0].msg) |
302 | |
303 | - @mock.patch('urllib2.urlopen') |
304 | + @mock.patch('requests.get') |
305 | @mock.patch('ci_cli.utils.load_config', side_effect=mock_load_config()) |
306 | def test_ts_server_returns_other_httperror(self, mock_load_config, |
307 | - mock_urlopen): |
308 | + mock_requests): |
309 | """Test cli response to ts server returning HTTPError.""" |
310 | - mock_urlopen.side_effect = urllib2.HTTPError(None, 403, "Forbidden", |
311 | - None, None) |
312 | + mock_requests.side_effect = [ |
313 | + FakeRequestsReturn(403, '', 'Forbidden') |
314 | + ] |
315 | args = ['status'] |
316 | with LogCapture() as lc: |
317 | logger = logging.getLogger() |
318 | self.cli.main(args, log=logger) |
319 | - mock_urlopen.assert_called_once() |
320 | - self.assertEquals("HTTP Error 403: Forbidden", lc.records[0].msg) |
321 | + mock_requests.assert_called_once() |
322 | + self.assertEquals( |
323 | + "Cannot reach the server at {}. Please, check your " |
324 | + "configuration file ({}): is 'ci_url' correctly set? " |
325 | + "Is the server up and reachable from this machine? " |
326 | + "(403 Client Error: Forbidden).".format( |
327 | + utils.CI_URL, utils.DEF_CFG), lc.records[0].msg) |
328 | |
329 | |
330 | class CliArgumentsTestCase(MainScriptTestCase): |
331 | |
332 | === modified file 'cli/ci_cli/tests/test_get_ticket_status.py' |
333 | --- cli/ci_cli/tests/test_get_ticket_status.py 2014-05-12 15:49:28 +0000 |
334 | +++ cli/ci_cli/tests/test_get_ticket_status.py 2014-05-27 09:44:30 +0000 |
335 | @@ -17,11 +17,11 @@ |
336 | |
337 | import mock |
338 | import json |
339 | -import urllib2 |
340 | |
341 | from ci_cli.tests import ( |
342 | capture_stdout, |
343 | MainScriptTestCase, |
344 | + FakeRequestsReturn, |
345 | ) |
346 | from ci_utils.ticket_states import ( |
347 | TicketWorkflowStep, |
348 | @@ -31,8 +31,8 @@ |
349 | |
350 | class GetTicketStatus(MainScriptTestCase): |
351 | |
352 | - @mock.patch('urllib2.urlopen') |
353 | - def test_get_single_ticket_status(self, mock_urlopen): |
354 | + @mock.patch('requests.get') |
355 | + def test_get_single_ticket_status(self, mock_requests): |
356 | data = { |
357 | "current_workflow_step": str(TicketWorkflowStep.IMAGE_BUILDING), |
358 | "status": str(TicketWorkflowStepStatus.INPROGRESS), |
359 | @@ -46,9 +46,9 @@ |
360 | "added_binaries": "mynewpackage", |
361 | "removed_binaries": "myoldpackage", |
362 | } |
363 | - resp = mock.Mock() |
364 | - resp.read.return_value = json.dumps(data) |
365 | - mock_urlopen.return_value = resp |
366 | + mock_requests.side_effect = [ |
367 | + FakeRequestsReturn(200, json.dumps(data)) |
368 | + ] |
369 | args = self.cli.parse_arguments(['status', '-t', '100']) |
370 | with capture_stdout(args.func, args) as cm: |
371 | self.assertEqual( |
372 | @@ -64,10 +64,10 @@ |
373 | str(TicketWorkflowStep.IMAGE_BUILDING), |
374 | str(TicketWorkflowStepStatus.INPROGRESS)), |
375 | cm) |
376 | - mock_urlopen.assert_called_once() |
377 | + mock_requests.assert_called_once() |
378 | |
379 | - @mock.patch('urllib2.urlopen') |
380 | - def test_get_ticket_status_unicode(self, mock_urlopen): |
381 | + @mock.patch('requests.get') |
382 | + def test_get_ticket_status_unicode(self, mock_requests): |
383 | data = { |
384 | "current_workflow_step": str(TicketWorkflowStep.IMAGE_BUILDING), |
385 | "status": str(TicketWorkflowStepStatus.INPROGRESS), |
386 | @@ -81,9 +81,9 @@ |
387 | "added_binaries": "mynewpackage", |
388 | "removed_binaries": "myoldpackage", |
389 | } |
390 | - resp = mock.Mock() |
391 | - resp.read.return_value = json.dumps(data) |
392 | - mock_urlopen.return_value = resp |
393 | + mock_requests.side_effect = [ |
394 | + FakeRequestsReturn(200, json.dumps(data)) |
395 | + ] |
396 | args = self.cli.parse_arguments(['status', '-t', '100']) |
397 | with capture_stdout(args.func, args) as cm: |
398 | self.assertEqual( |
399 | @@ -99,10 +99,10 @@ |
400 | str(TicketWorkflowStep.IMAGE_BUILDING), |
401 | str(TicketWorkflowStepStatus.INPROGRESS)), |
402 | cm) |
403 | - mock_urlopen.assert_called_once() |
404 | + mock_requests.assert_called_once() |
405 | |
406 | - @mock.patch('urllib2.urlopen') |
407 | - def test_get_single_failed_ticket_status(self, mock_urlopen): |
408 | + @mock.patch('requests.get') |
409 | + def test_get_single_failed_ticket_status(self, mock_requests): |
410 | data = { |
411 | "current_workflow_step": str(TicketWorkflowStep.FAILED), |
412 | "status": str(TicketWorkflowStepStatus.FAILED), |
413 | @@ -116,9 +116,9 @@ |
414 | "added_binaries": "mynewpackage", |
415 | "removed_binaries": "myoldpackage", |
416 | } |
417 | - resp = mock.Mock() |
418 | - resp.read.return_value = json.dumps(data) |
419 | - mock_urlopen.return_value = resp |
420 | + mock_requests.side_effect = [ |
421 | + FakeRequestsReturn(200, json.dumps(data)) |
422 | + ] |
423 | args = self.cli.parse_arguments(['status', '-t', '100']) |
424 | with capture_stdout(args.func, args) as cm: |
425 | self.assertEqual( |
426 | @@ -133,10 +133,10 @@ |
427 | 'Removed binaries: myoldpackage\n'.format( |
428 | str(TicketWorkflowStepStatus.FAILED)), |
429 | cm) |
430 | - mock_urlopen.assert_called_once() |
431 | + mock_requests.assert_called_once() |
432 | |
433 | - @mock.patch('urllib2.urlopen') |
434 | - def test_get_all_ticket_status(self, mock_urlopen): |
435 | + @mock.patch('requests.get') |
436 | + def test_get_all_ticket_status(self, mock_requests): |
437 | data = { |
438 | "objects": [ |
439 | { |
440 | @@ -165,9 +165,9 @@ |
441 | }, |
442 | ] |
443 | } |
444 | - resp = mock.Mock() |
445 | - resp.read.return_value = json.dumps(data) |
446 | - mock_urlopen.return_value = resp |
447 | + mock_requests.side_effect = [ |
448 | + FakeRequestsReturn(200, json.dumps(data)) |
449 | + ] |
450 | args = self.cli.parse_arguments(['status']) |
451 | with capture_stdout(args.func, args) as cm: |
452 | self.assertEqual( |
453 | @@ -176,15 +176,15 @@ |
454 | "Ticket #7, Upstream bug fix is 100 110\n" + |
455 | "Ticket #9, Other new feature is 100 110\n", |
456 | cm) |
457 | - mock_urlopen.assert_called_once() |
458 | + mock_requests.assert_called_once() |
459 | |
460 | - @mock.patch('urllib2.urlopen') |
461 | - def test_get_status_404_response(self, mock_urlopen): |
462 | - mock_urlopen.side_effect = urllib2.HTTPError("http://example.com", |
463 | - 404, "Not Found", "", |
464 | - None) |
465 | + @mock.patch('requests.get') |
466 | + def test_get_status_404_response(self, mock_requests): |
467 | + mock_requests.side_effect = [ |
468 | + FakeRequestsReturn(404, '', 'Not Found') |
469 | + ] |
470 | args = self.cli.parse_arguments(['status', '-t', '99']) |
471 | with self.assertRaises(SystemExit) as cm: |
472 | args.func(args) |
473 | - mock_urlopen.assert_called_once() |
474 | + mock_requests.assert_called_once() |
475 | self.assertEqual("Ticket number 99 not found.", cm.exception.message) |
476 | |
477 | === modified file 'cli/ci_cli/tests/test_image.py' |
478 | --- cli/ci_cli/tests/test_image.py 2014-05-07 14:56:11 +0000 |
479 | +++ cli/ci_cli/tests/test_image.py 2014-05-27 09:44:30 +0000 |
480 | @@ -26,26 +26,10 @@ |
481 | from ci_cli.tests import ( |
482 | capture_stdout, |
483 | MainScriptTestCase, |
484 | + FakeRequestsReturn, |
485 | ) |
486 | |
487 | |
488 | -class FakeUrlOpenReturn: |
489 | - """Object to mock the return of urllib2.urlopen.""" |
490 | - |
491 | - def __init__(self, code, content): |
492 | - self.code = code |
493 | - self.content = content |
494 | - |
495 | - def __repr__(self): |
496 | - return self.content |
497 | - |
498 | - def __iter__(self): |
499 | - return (line for line in self.content.split("\n")) |
500 | - |
501 | - def read(self): |
502 | - return self.content |
503 | - |
504 | - |
505 | artifact_data = { |
506 | "objects": [ |
507 | { |
508 | @@ -88,11 +72,11 @@ |
509 | if os.path.exists(self.image_path): |
510 | os.remove(self.image_path) |
511 | |
512 | - @mock.patch('urllib2.urlopen') |
513 | + @mock.patch('requests.get') |
514 | @mock.patch('ci_utils.data_store.create_for_ticket') |
515 | - def test_get_ticket_image(self, mock_create_datastore, mock_urlopen): |
516 | - mock_urlopen.side_effect = [ |
517 | - FakeUrlOpenReturn(200, json.dumps(artifact_data))] |
518 | + def test_get_ticket_image(self, mock_create_datastore, mock_requests): |
519 | + mock_requests.side_effect = [ |
520 | + FakeRequestsReturn(200, json.dumps(artifact_data))] |
521 | mock_datastore = mock.Mock() |
522 | mock_datastore.get_file.return_value = buffer(" ") |
523 | mock_create_datastore.return_value = mock_datastore |
524 | @@ -106,12 +90,12 @@ |
525 | "Image file can be found at: {}\n".format(self.image_path)) |
526 | mock_datastore.assert_called_once() |
527 | |
528 | - @mock.patch('urllib2.urlopen') |
529 | + @mock.patch('requests.get') |
530 | @mock.patch('ci_utils.data_store.create_for_ticket') |
531 | def test_get_ticket_image_not_found(self, mock_create_datastore, |
532 | - mock_urlopen): |
533 | - mock_urlopen.side_effect = [ |
534 | - FakeUrlOpenReturn(200, json.dumps(artifact_data))] |
535 | + mock_requests): |
536 | + mock_requests.side_effect = [ |
537 | + FakeRequestsReturn(200, json.dumps(artifact_data))] |
538 | mock_datastore = mock.Mock() |
539 | mock_datastore.get_file.side_effect = data_store.DataStoreException( |
540 | "Failed to get file: d62d3d9a-ac3d-11e3-847d-fa163eaf5928.img, " |
541 | @@ -125,12 +109,12 @@ |
542 | with self.assertRaises(ImageObjectNotFound): |
543 | args.func(args) |
544 | |
545 | - @mock.patch('urllib2.urlopen') |
546 | + @mock.patch('requests.get') |
547 | @mock.patch('ci_utils.data_store.DataStore') |
548 | def test_get_ticket_image_already_exists(self, mock_data_store, |
549 | - mock_urlopen): |
550 | - mock_urlopen.side_effect = [ |
551 | - FakeUrlOpenReturn(200, json.dumps(artifact_data))] |
552 | + mock_requests): |
553 | + mock_requests.side_effect = [ |
554 | + FakeRequestsReturn(200, json.dumps(artifact_data))] |
555 | open(self.image_path, 'a').close() |
556 | args = self.cli.parse_arguments(['get_image', '-t', '4', '-n', |
557 | self.image_path]) |
558 | @@ -139,13 +123,13 @@ |
559 | "{} already exists.\n".format(self.image_path), |
560 | cm) |
561 | |
562 | - @mock.patch('urllib2.urlopen') |
563 | + @mock.patch('requests.get') |
564 | @mock.patch('ci_utils.data_store.create_for_ticket') |
565 | def test_get_last_completed_ticket_image(self, mock_create_datastore, |
566 | - mock_urlopen): |
567 | - mock_urlopen.side_effect = [ |
568 | - FakeUrlOpenReturn(200, json.dumps(ticket_data)), |
569 | - FakeUrlOpenReturn(200, json.dumps(artifact_data)) |
570 | + mock_requests): |
571 | + mock_requests.side_effect = [ |
572 | + FakeRequestsReturn(200, json.dumps(ticket_data)), |
573 | + FakeRequestsReturn(200, json.dumps(artifact_data)) |
574 | ] |
575 | mock_datastore = mock.Mock() |
576 | mock_datastore.get_file.return_value = buffer(" ") |
577 | @@ -159,11 +143,11 @@ |
578 | "Image file can be found at: {}\n".format(self.image_path)) |
579 | mock_datastore.assert_called_once() |
580 | |
581 | - @mock.patch('urllib2.urlopen') |
582 | - def test_get_last_completed_ticket_none_complete(self, mock_urlopen): |
583 | + @mock.patch('requests.get') |
584 | + def test_get_last_completed_ticket_none_complete(self, mock_requests): |
585 | data = {'objects': []} |
586 | - mock_urlopen.side_effect = [ |
587 | - FakeUrlOpenReturn(200, json.dumps(data))] |
588 | + mock_requests.side_effect = [ |
589 | + FakeRequestsReturn(200, json.dumps(data))] |
590 | args = self.cli.parse_arguments(['get_image', '-n', self.image_path]) |
591 | with self.assertRaises(SystemExit) as cm: |
592 | args.func(args) |
593 | |
594 | === modified file 'cli/ci_cli/tests/test_utils.py' |
595 | --- cli/ci_cli/tests/test_utils.py 2014-05-07 17:57:12 +0000 |
596 | +++ cli/ci_cli/tests/test_utils.py 2014-05-27 09:44:30 +0000 |
597 | @@ -21,6 +21,7 @@ |
598 | import tempfile |
599 | |
600 | from ci_cli import utils |
601 | +from ci_cli.tests import FakeRequestsReturn |
602 | from ci_utils.testing import TestCaseWithGnupg |
603 | |
604 | |
605 | @@ -47,24 +48,24 @@ |
606 | location = 'http://www.example.com/api/v1/ticket/abc/123/' |
607 | self.assertRaises(ValueError, utils.parse_id, location) |
608 | |
609 | - @mock.patch('urllib2.urlopen') |
610 | - def test_get_sourcepackage_uri(self, mock_urlopen): |
611 | + @mock.patch('requests.get') |
612 | + def test_get_sourcepackage_uri(self, mock_requests): |
613 | data = {"objects": [{"resource_uri": "/api/v1/sourcepackage/4/"}]} |
614 | - resp = mock.Mock() |
615 | - resp.read.return_value = json.dumps(data) |
616 | - mock_urlopen.return_value = resp |
617 | + mock_requests.side_effect = [ |
618 | + FakeRequestsReturn(200, json.dumps(data)) |
619 | + ] |
620 | uri = utils.get_sourcepackage_uri('foobar') |
621 | - mock_urlopen.assert_called_once() |
622 | + mock_requests.assert_called_once() |
623 | self.assertEquals(uri, '/api/v1/sourcepackage/4/') |
624 | |
625 | - @mock.patch('urllib2.urlopen') |
626 | - def test_get_sourcepackage_uri_not_found(self, mock_urlopen): |
627 | + @mock.patch('requests.get') |
628 | + def test_get_sourcepackage_uri_not_found(self, mock_requests): |
629 | data = {"objects": []} |
630 | - resp = mock.Mock() |
631 | - resp.read.return_value = json.dumps(data) |
632 | - mock_urlopen.return_value = resp |
633 | + mock_requests.side_effect = [ |
634 | + FakeRequestsReturn(200, json.dumps(data)) |
635 | + ] |
636 | uri = utils.get_sourcepackage_uri('foobar') |
637 | - mock_urlopen.assert_called_once() |
638 | + mock_requests.assert_called_once() |
639 | self.assertEquals(uri, '') |
640 | |
641 | def test_load_config(self): |
642 | |
643 | === modified file 'cli/ci_cli/utils.py' |
644 | --- cli/ci_cli/utils.py 2014-04-08 13:27:01 +0000 |
645 | +++ cli/ci_cli/utils.py 2014-05-27 09:44:30 +0000 |
646 | @@ -17,8 +17,8 @@ |
647 | import json |
648 | import logging |
649 | import sys |
650 | -import urllib2 |
651 | import yaml |
652 | +import requests |
653 | |
654 | from ci_utils import data_store |
655 | |
656 | @@ -72,22 +72,19 @@ |
657 | |
658 | |
659 | def post(url, data, headers=HEADERS, patch=False): |
660 | - req = urllib2.Request(url=url, data=json.dumps(data), headers=HEADERS) |
661 | - f = urllib2.urlopen(req) |
662 | - return f.info().dict['location'] |
663 | + req = requests.post(url=url, data=json.dumps(data), headers=HEADERS) |
664 | + resp = req.headers |
665 | + return resp['location'] |
666 | |
667 | |
668 | def patch(url, data, headers=HEADERS): |
669 | - req = urllib2.Request(url=url, data=json.dumps(data), headers=HEADERS) |
670 | - req.get_method = lambda: 'PATCH' |
671 | - urllib2.urlopen(req) |
672 | + requests.patch(url=url, data=json.dumps(data), headers=HEADERS) |
673 | |
674 | |
675 | def get(url): |
676 | - f = urllib2.urlopen(url) |
677 | - |
678 | - json_data = f.read() |
679 | - return json.loads(json_data) |
680 | + r = requests.get(url) |
681 | + r.raise_for_status() |
682 | + return r.json() |
683 | |
684 | |
685 | def parse_id(location): |
686 | @@ -101,9 +98,7 @@ |
687 | |
688 | def get_sourcepackage_uri(sourcepackage): |
689 | url = CI_URL + SOURCEPACKAGE_BASE + '?name__iexact=' + sourcepackage |
690 | - f = urllib2.urlopen(url) |
691 | - json_data = f.read() |
692 | - data = json.loads(json_data) |
693 | + data = get(url) |
694 | try: |
695 | sourcepackage_uri = data['objects'][0]['resource_uri'] |
696 | return sourcepackage_uri |
697 | |
698 | === modified file 'cli/setup.py' |
699 | --- cli/setup.py 2014-05-07 17:33:32 +0000 |
700 | +++ cli/setup.py 2014-05-27 09:44:30 +0000 |
701 | @@ -27,6 +27,7 @@ |
702 | 'dput>=1.6', |
703 | 'lazr.enum>=1.1.2', |
704 | 'PyYAML==3.10', |
705 | + 'requests==2.2.1', |
706 | 'mock==1.0.1', |
707 | 'testfixtures>=3.0.1', |
708 | 'python-gnupg>=0.3.6', |
709 | |
710 | === modified file 'cli/ubuntu-ci' |
711 | --- cli/ubuntu-ci 2014-05-07 14:56:11 +0000 |
712 | +++ cli/ubuntu-ci 2014-05-27 09:44:30 +0000 |
713 | @@ -19,7 +19,8 @@ |
714 | import logging |
715 | import os |
716 | import sys |
717 | -import urllib2 |
718 | + |
719 | +from requests import exceptions |
720 | |
721 | from ci_cli import ( |
722 | ticket, |
723 | @@ -78,8 +79,8 @@ |
724 | help='Ticket to display status of. Leave off ' |
725 | 'for last successful ticket') |
726 | image_parser.add_argument('-n', '--name', |
727 | - help='Desired file name (and path) for the ' |
728 | - 'downloaded image', required=True) |
729 | + help='Desired file name (and path) for the ' |
730 | + 'downloaded image', required=True) |
731 | image_parser.set_defaults(func=image.get_image) |
732 | return parser.parse_args(args) |
733 | |
734 | @@ -153,10 +154,16 @@ |
735 | except image.ImageObjectNotFound as exc: |
736 | log.error("Image cannot be downloaded: {} ({})".format(exc.filename, |
737 | str(exc))) |
738 | - except urllib2.URLError as exc: |
739 | - if isinstance(exc, urllib2.HTTPError): |
740 | - reason = exc.reason |
741 | - code = exc.code |
742 | + except exceptions.RequestException as exc: |
743 | + error = ("Cannot reach the server at {}. Please, check your " |
744 | + "configuration file ({}): is 'ci_url' correctly set? " |
745 | + "Is the server up and reachable from this machine? " |
746 | + "({}).".format(utils.CI_URL, utils.DEF_CFG, str(exc))) |
747 | + if isinstance(exc, exceptions.HTTPError): |
748 | + code = None |
749 | + if exc.response: |
750 | + code = exc.response.status_code |
751 | + |
752 | if code == 500: |
753 | log.error("Server at {} reported internal error. Maybe a " |
754 | "database hiccup?".format(utils.CI_URL)) |
755 | @@ -164,13 +171,9 @@ |
756 | log.error("Server at {} not found. Is 'ci_url' correctly " |
757 | "set? ({})".format(utils.CI_URL, str(exc))) |
758 | else: |
759 | - log.error(str(exc)) |
760 | + log.error(error) |
761 | else: |
762 | - reason = exc.reason |
763 | - log.error("Cannot reach the server at {}. Please, check your " |
764 | - "configuration file ({}): is 'ci_url' correctly set? " |
765 | - "Is the server up and reachable from this machine? " |
766 | - "({}).".format(utils.CI_URL, utils.DEF_CFG, reason)) |
767 | + log.error(error) |
768 | except data_store.DataStoreException as exc: |
769 | log.error("Data Store Error: {}".format(exc)) |
770 | except Exception: |
771 | |
772 | === modified file 'debian/control' |
773 | --- debian/control 2014-05-15 16:12:18 +0000 |
774 | +++ debian/control 2014-05-27 09:44:30 +0000 |
775 | @@ -32,6 +32,7 @@ |
776 | python-lazr.enum (>= 1.1.2-0~), |
777 | python-yaml (>= 3.10-2), |
778 | python-swiftclient, |
779 | + python-requests (>=2.2.1), |
780 | uci-utils (= ${binary:Version}), |
781 | ${misc:Depends}, |
782 | ${python:Depends} |
FAILED: Continuous integration, rev:499 /code.launchpad .net/~cjohnston /uci-engine/ cli-requests/ +merge/ 220960/ +edit-commit- message
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
http:// s-jenkins. ubuntu- ci:8080/ job/uci- engine- ci/701/
Executed test runs:
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/uci- engine- ci/701/ rebuild
http://