def test_get_timeout(self):
api_client = self.mock_keep_services(('keep', 10, False, 'disk'))
- keep_client = arvados.KeepClient(api_client=api_client)
force_timeout = [socket.timeout("timed out")]
- with mock.patch('requests.get', side_effect=force_timeout) as mock_request:
+ with tutil.mock_get(force_timeout) as mock_session:
+ keep_client = arvados.KeepClient(api_client=api_client)
with self.assertRaises(arvados.errors.KeepReadError):
keep_client.get('ffffffffffffffffffffffffffffffff')
- self.assertTrue(mock_request.called)
+ self.assertTrue(mock_session.return_value.get.called)
self.assertEqual(
arvados.KeepClient.DEFAULT_TIMEOUT,
- mock_request.call_args[1]['timeout'])
+ mock_session.return_value.get.call_args[1]['timeout'])
def test_put_timeout(self):
api_client = self.mock_keep_services(('keep', 10, False, 'disk'))
- keep_client = arvados.KeepClient(api_client=api_client)
force_timeout = [socket.timeout("timed out")]
- with mock.patch('requests.put', side_effect=force_timeout) as mock_request:
+ with tutil.mock_put(force_timeout) as mock_session:
+ keep_client = arvados.KeepClient(api_client=api_client)
with self.assertRaises(arvados.errors.KeepWriteError):
keep_client.put('foo')
- self.assertTrue(mock_request.called)
+ self.assertTrue(mock_session.return_value.put.called)
self.assertEqual(
arvados.KeepClient.DEFAULT_TIMEOUT,
- mock_request.call_args[1]['timeout'])
+ mock_session.return_value.put.call_args[1]['timeout'])
def test_proxy_get_timeout(self):
# Force a timeout, verifying that the requests.get or
# requests.put method was called with the proxy_timeout
# setting rather than the default timeout.
api_client = self.mock_keep_services(('keep', 10, False, 'proxy'))
- keep_client = arvados.KeepClient(api_client=api_client)
force_timeout = [socket.timeout("timed out")]
- with mock.patch('requests.get', side_effect=force_timeout) as mock_request:
+ with tutil.mock_get(force_timeout) as mock_session:
+ keep_client = arvados.KeepClient(api_client=api_client)
with self.assertRaises(arvados.errors.KeepReadError):
keep_client.get('ffffffffffffffffffffffffffffffff')
- self.assertTrue(mock_request.called)
+ self.assertTrue(mock_session.return_value.get.called)
self.assertEqual(
arvados.KeepClient.DEFAULT_PROXY_TIMEOUT,
- mock_request.call_args[1]['timeout'])
+ mock_session.return_value.get.call_args[1]['timeout'])
def test_proxy_put_timeout(self):
# Force a timeout, verifying that the requests.get or
# requests.put method was called with the proxy_timeout
# setting rather than the default timeout.
api_client = self.mock_keep_services(('keep', 10, False, 'proxy'))
- keep_client = arvados.KeepClient(api_client=api_client)
force_timeout = [socket.timeout("timed out")]
- with mock.patch('requests.put', side_effect=force_timeout) as mock_request:
+ with tutil.mock_put(force_timeout) as mock_session:
+ keep_client = arvados.KeepClient(api_client=api_client)
with self.assertRaises(arvados.errors.KeepWriteError):
keep_client.put('foo')
- self.assertTrue(mock_request.called)
+ self.assertTrue(mock_session.return_value.put.called)
self.assertEqual(
arvados.KeepClient.DEFAULT_PROXY_TIMEOUT,
- mock_request.call_args[1]['timeout'])
+ mock_session.return_value.put.call_args[1]['timeout'])
def test_probe_order_reference_set(self):
# expected_order[i] is the probe order for
min_penalty,
max_penalty))
+ def check_64_zeros_error_order(self, verb, exc_class):
+ data = '0' * 64
+ if verb == 'get':
+ data = hashlib.md5(data).hexdigest() + '+1234'
+ api_client = self.mock_n_keep_disks(16)
+ keep_client = arvados.KeepClient(api_client=api_client)
+ with mock.patch('requests.' + verb,
+ side_effect=socket.timeout) as req_mock, \
+ self.assertRaises(exc_class) as err_check:
+ getattr(keep_client, verb)(data)
+ urls = [urlparse.urlparse(url)
+ for url in err_check.exception.service_errors()]
+ self.assertEqual([('keep0x' + c, 80) for c in '3eab2d5fc9681074'],
+ [(url.hostname, url.port) for url in urls])
+
+ def test_get_error_shows_probe_order(self):
+ self.check_64_zeros_error_order('get', arvados.errors.KeepReadError)
+
+ def test_put_error_shows_probe_order(self):
+ self.check_64_zeros_error_order('put', arvados.errors.KeepWriteError)
+
+ def check_no_services_error(self, verb, exc_class):
+ api_client = mock.MagicMock(name='api_client')
+ api_client.keep_services().accessible().execute.side_effect = (
+ arvados.errors.ApiError)
+ keep_client = arvados.KeepClient(api_client=api_client)
+ with self.assertRaises(exc_class) as err_check:
+ getattr(keep_client, verb)('d41d8cd98f00b204e9800998ecf8427e+0')
+ self.assertEqual(0, len(err_check.exception.service_errors()))
+
+ def test_get_error_with_no_services(self):
+ self.check_no_services_error('get', arvados.errors.KeepReadError)
+
+ def test_put_error_with_no_services(self):
+ self.check_no_services_error('put', arvados.errors.KeepWriteError)
+
+ def check_errors_from_last_retry(self, verb, exc_class):
+ api_client = self.mock_n_keep_disks(2)
+ req_mock = getattr(tutil, 'mock_{}_responses'.format(verb))(
+ "retry error reporting test", 500, 500, 403, 403)
+ with req_mock, tutil.skip_sleep, \
+ self.assertRaises(exc_class) as err_check:
+ keep_client = arvados.KeepClient(api_client=api_client)
+ getattr(keep_client, verb)('d41d8cd98f00b204e9800998ecf8427e+0',
+ num_retries=3)
+ self.assertEqual([403, 403], [
+ getattr(error, 'status_code', None)
+ for error in err_check.exception.service_errors().itervalues()])
+
+ def test_get_error_reflects_last_retry(self):
+ self.check_errors_from_last_retry('get', arvados.errors.KeepReadError)
+
+ def test_put_error_reflects_last_retry(self):
+ self.check_errors_from_last_retry('put', arvados.errors.KeepWriteError)
+
+ def test_put_error_does_not_include_successful_puts(self):
+ data = 'partial failure test'
+ data_loc = '{}+{}'.format(hashlib.md5(data).hexdigest(), len(data))
+ api_client = self.mock_n_keep_disks(3)
+ with tutil.mock_put_responses(data_loc, 200, 500, 500) as req_mock, \
+ self.assertRaises(arvados.errors.KeepWriteError) as exc_check:
+ keep_client = arvados.KeepClient(api_client=api_client)
+ keep_client.put(data)
+ self.assertEqual(2, len(exc_check.exception.service_errors()))
+
class KeepClientRetryTestMixin(object):
# Testing with a local Keep store won't exercise the retry behavior.
def setUp(self):
self.client_kwargs = {'proxy': self.PROXY_ADDR, 'local_store': ''}
- self.mock_session = None
-
- def mock_get_responses(self, body, *codes, **headers):
- self.mock_session = tutil.MockSession(body, codes, headers)
-
- def mock_put_responses(self, body, *codes, **headers):
- self.mock_session = tutil.MockSession(body, codes, headers)
def new_client(self, **caller_kwargs):
kwargs = self.client_kwargs.copy()
kwargs.update(caller_kwargs)
- return arvados.KeepClient(session=self.mock_session, **kwargs)
+ return arvados.KeepClient(**kwargs)
def run_method(self, *args, **kwargs):
raise NotImplementedError("test subclasses must define run_method")
self.assertRaises(error_class, self.run_method, *args, **kwargs)
def test_immediate_success(self):
- self.TEST_PATCHER(self.DEFAULT_EXPECT, 200)
- self.check_success()
+ with self.TEST_PATCHER(self.DEFAULT_EXPECT, 200):
+ self.check_success()
def test_retry_then_success(self):
- self.TEST_PATCHER(self.DEFAULT_EXPECT, 500, 200)
- self.check_success(num_retries=3)
+ with self.TEST_PATCHER(self.DEFAULT_EXPECT, 500, 200):
+ self.check_success(num_retries=3)
def test_no_default_retry(self):
- self.TEST_PATCHER(self.DEFAULT_EXPECT, 500, 200)
- self.check_exception()
+ with self.TEST_PATCHER(self.DEFAULT_EXPECT, 500, 200):
+ self.check_exception()
def test_no_retry_after_permanent_error(self):
- self.TEST_PATCHER(self.DEFAULT_EXPECT, 403, 200)
- self.check_exception(num_retries=3)
+ with self.TEST_PATCHER(self.DEFAULT_EXPECT, 403, 200):
+ self.check_exception(num_retries=3)
def test_error_after_retries_exhausted(self):
- self.TEST_PATCHER(self.DEFAULT_EXPECT, 500, 500, 200)
- self.check_exception(num_retries=1)
+ with self.TEST_PATCHER(self.DEFAULT_EXPECT, 500, 500, 200):
+ self.check_exception(num_retries=1)
def test_num_retries_instance_fallback(self):
self.client_kwargs['num_retries'] = 3
- self.TEST_PATCHER(self.DEFAULT_EXPECT, 500, 200)
- self.check_success()
+ with self.TEST_PATCHER(self.DEFAULT_EXPECT, 500, 200):
+ self.check_success()
@tutil.skip_sleep
DEFAULT_EXPECT = KeepClientRetryTestMixin.TEST_DATA
DEFAULT_EXCEPTION = arvados.errors.KeepReadError
HINTED_LOCATOR = KeepClientRetryTestMixin.TEST_LOCATOR + '+K@xyzzy'
- TEST_PATCHER = KeepClientRetryTestMixin.mock_get_responses
+ TEST_PATCHER = staticmethod(tutil.mock_get_responses)
def run_method(self, locator=KeepClientRetryTestMixin.TEST_LOCATOR,
*args, **kwargs):
return self.new_client().get(locator, *args, **kwargs)
def test_specific_exception_when_not_found(self):
- self.mock_get_responses(self.DEFAULT_EXPECT, 404, 200)
- self.check_exception(arvados.errors.NotFoundError, num_retries=3)
+ with tutil.mock_get_responses(self.DEFAULT_EXPECT, 404, 200):
+ self.check_exception(arvados.errors.NotFoundError, num_retries=3)
def test_general_exception_with_mixed_errors(self):
# get should raise a NotFoundError if no server returns the block,
# This test rigs up 50/50 disagreement between two servers, and
# checks that it does not become a NotFoundError.
client = self.new_client()
- self.mock_get_responses(self.DEFAULT_EXPECT, 404, 500)
- with self.assertRaises(arvados.errors.KeepReadError) as exc_check:
- client.get(self.HINTED_LOCATOR)
- self.assertNotIsInstance(
- exc_check.exception, arvados.errors.NotFoundError,
- "mixed errors raised NotFoundError")
+ with tutil.mock_get_responses(self.DEFAULT_EXPECT, 404, 500):
+ with self.assertRaises(arvados.errors.KeepReadError) as exc_check:
+ client.get(self.HINTED_LOCATOR)
+ self.assertNotIsInstance(
+ exc_check.exception, arvados.errors.NotFoundError,
+ "mixed errors raised NotFoundError")
def test_hint_server_can_succeed_without_retries(self):
- self.mock_get_responses(self.DEFAULT_EXPECT, 404, 200, 500)
- self.check_success(locator=self.HINTED_LOCATOR)
+ with tutil.mock_get_responses(self.DEFAULT_EXPECT, 404, 200, 500):
+ self.check_success(locator=self.HINTED_LOCATOR)
def test_try_next_server_after_timeout(self):
- side_effects = [
- socket.timeout("timed out"),
- tutil.fake_requests_response(200, self.DEFAULT_EXPECT)]
- with mock.patch('requests.get',
- side_effect=iter(side_effects)):
+ with tutil.mock_get([
+ socket.timeout("timed out"),
+ tutil.fake_requests_response(200, self.DEFAULT_EXPECT)]):
self.check_success(locator=self.HINTED_LOCATOR)
def test_retry_data_with_wrong_checksum(self):
- self.mock_get_responses(['baddata', self.TEST_DATA], 200, 200)
- self.check_success(locator=self.HINTED_LOCATOR)
+ with tutil.mock_get((tutil.fake_requests_response(200, s) for s in ['baddata', self.TEST_DATA])):
+ self.check_success(locator=self.HINTED_LOCATOR)
@tutil.skip_sleep
class KeepClientRetryPutTestCase(KeepClientRetryTestMixin, unittest.TestCase):
DEFAULT_EXPECT = KeepClientRetryTestMixin.TEST_LOCATOR
DEFAULT_EXCEPTION = arvados.errors.KeepWriteError
- TEST_PATCHER = KeepClientRetryTestMixin.mock_put_responses
+ TEST_PATCHER = staticmethod(tutil.mock_put_responses)
def run_method(self, data=KeepClientRetryTestMixin.TEST_DATA,
copies=1, *args, **kwargs):
return self.new_client().put(data, copies, *args, **kwargs)
def test_do_not_send_multiple_copies_to_same_server(self):
- self.mock_put_responses(self.DEFAULT_EXPECT, 200)
- self.check_exception(copies=2, num_retries=3)
+ with tutil.mock_put_responses(self.DEFAULT_EXPECT, 200):
+ self.check_exception(copies=2, num_retries=3)