Merge "test-expirer: call tracking & exception stubs"
This commit is contained in:
commit
6221e5d8c1
@ -61,6 +61,9 @@ class FakeInternalClient(object):
|
|||||||
'container2: [],
|
'container2: [],
|
||||||
},
|
},
|
||||||
'account2': {},
|
'account2': {},
|
||||||
|
'account3': {
|
||||||
|
'some_bad_container': UnexpectedResponse(),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
N.B. the objects entries should be the container-server JSON style
|
N.B. the objects entries should be the container-server JSON style
|
||||||
db rows, but this fake will dynamically detect when names are given
|
db rows, but this fake will dynamically detect when names are given
|
||||||
@ -68,11 +71,16 @@ class FakeInternalClient(object):
|
|||||||
"""
|
"""
|
||||||
self.aco_dict = defaultdict(dict)
|
self.aco_dict = defaultdict(dict)
|
||||||
self.aco_dict.update(aco_dict)
|
self.aco_dict.update(aco_dict)
|
||||||
|
self._calls = []
|
||||||
|
|
||||||
def get_account_info(self, account):
|
def get_account_info(self, account):
|
||||||
acc_dict = self.aco_dict[account]
|
acc_dict = self.aco_dict[account]
|
||||||
container_count = len(acc_dict)
|
container_count = len(acc_dict)
|
||||||
obj_count = sum(len(objs) for objs in acc_dict.values())
|
obj_count = 0
|
||||||
|
for obj_list_or_err in acc_dict.values():
|
||||||
|
if isinstance(obj_list_or_err, Exception):
|
||||||
|
continue
|
||||||
|
obj_count += len(obj_list_or_err)
|
||||||
return container_count, obj_count
|
return container_count, obj_count
|
||||||
|
|
||||||
def iter_containers(self, account, prefix=''):
|
def iter_containers(self, account, prefix=''):
|
||||||
@ -81,12 +89,19 @@ class FakeInternalClient(object):
|
|||||||
for container in sorted(acc_dict)
|
for container in sorted(acc_dict)
|
||||||
if container.startswith(prefix)]
|
if container.startswith(prefix)]
|
||||||
|
|
||||||
def delete_container(*a, **kw):
|
def delete_container(self, account, container, **kwargs):
|
||||||
pass
|
self._calls.append(
|
||||||
|
('delete_container', '/'.join((account, container)), kwargs)
|
||||||
|
)
|
||||||
|
|
||||||
def iter_objects(self, account, container, **kwargs):
|
def iter_objects(self, account, container, **kwargs):
|
||||||
|
self._calls.append(
|
||||||
|
('iter_objects', '/'.join((account, container)), kwargs)
|
||||||
|
)
|
||||||
acc_dict = self.aco_dict[account]
|
acc_dict = self.aco_dict[account]
|
||||||
obj_iter = acc_dict.get(container, [])
|
obj_iter = acc_dict.get(container, [])
|
||||||
|
if isinstance(obj_iter, Exception):
|
||||||
|
raise obj_iter
|
||||||
resp = []
|
resp = []
|
||||||
for obj in obj_iter:
|
for obj in obj_iter:
|
||||||
if not isinstance(obj, dict):
|
if not isinstance(obj, dict):
|
||||||
@ -94,8 +109,10 @@ class FakeInternalClient(object):
|
|||||||
resp.append(obj)
|
resp.append(obj)
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
def delete_object(*a, **kw):
|
def delete_object(self, account, container, obj, **kwargs):
|
||||||
pass
|
self._calls.append(
|
||||||
|
('delete_object', '/'.join((account, container, obj)), kwargs)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestExpirerHelpers(TestCase):
|
class TestExpirerHelpers(TestCase):
|
||||||
@ -231,7 +248,7 @@ class TestObjectExpirer(TestCase):
|
|||||||
|
|
||||||
self.ts = make_timestamp_iter()
|
self.ts = make_timestamp_iter()
|
||||||
|
|
||||||
now = int(time())
|
self.now = now = int(time())
|
||||||
|
|
||||||
self.empty_time = str(now - 864000)
|
self.empty_time = str(now - 864000)
|
||||||
self.empty_time_container = self.get_expirer_container(self.empty_time)
|
self.empty_time_container = self.get_expirer_container(self.empty_time)
|
||||||
@ -1585,38 +1602,34 @@ class TestObjectExpirer(TestCase):
|
|||||||
# Test that object listing on the first container returns 503 and raise
|
# Test that object listing on the first container returns 503 and raise
|
||||||
# UnexpectedResponse, and expect the second task container will
|
# UnexpectedResponse, and expect the second task container will
|
||||||
# continue to be processed.
|
# continue to be processed.
|
||||||
# In this test, all tasks are assigned to the tested expirer.
|
|
||||||
my_index = 0
|
|
||||||
divisor = 1
|
|
||||||
|
|
||||||
# Store reference to the real method before mocking
|
self.expirer.swift.aco_dict['.expiring_objects'][
|
||||||
real_iter_objects = self.fake_swift.iter_objects
|
self.just_past_time_container] = \
|
||||||
|
internal_client.UnexpectedResponse(
|
||||||
|
'Mocked error', Response(status=503))
|
||||||
|
|
||||||
def mock_iter_objects(account, container, **kwargs):
|
with mock.patch.object(self.expirer, 'pop_queue'):
|
||||||
if container == self.just_past_time_container:
|
self.expirer.run_once()
|
||||||
mock_resp = Response(status=503)
|
# everything but the broken container
|
||||||
raise internal_client.UnexpectedResponse(
|
expected = sorted(
|
||||||
'Mocked error', mock_resp)
|
p
|
||||||
return real_iter_objects(account, container)
|
for c, paths in self.expired_target_paths.items()
|
||||||
|
for p in paths
|
||||||
task_account_container_list = [
|
if c != self.just_past_time
|
||||||
('.expiring_objects', self.just_past_time_container),
|
)
|
||||||
('.expiring_objects', self.past_time_container)
|
self.assertEqual(
|
||||||
]
|
expected, sorted(
|
||||||
expected = [
|
path
|
||||||
self.make_task(self.past_time_container, self.past_time,
|
for method, path, kwargs in self.expirer.swift._calls
|
||||||
target_path)
|
if method == 'delete_object'
|
||||||
for target_path in self.expired_target_paths[self.past_time]]
|
))
|
||||||
|
self.assertEqual(
|
||||||
with mock.patch.object(self.expirer.swift, 'iter_objects',
|
[('.expiring_objects/%s' % self.empty_time_container,
|
||||||
mock_iter_objects):
|
{'acceptable_statuses': (2, 404, 409)})], [
|
||||||
with mock.patch.object(self.expirer.swift, 'delete_container') \
|
(path, kwargs)
|
||||||
as mock_delete_container:
|
for method, path, kwargs in self.expirer.swift._calls
|
||||||
self.assertEqual(
|
if method == 'delete_container'
|
||||||
list(self.expirer.iter_task_to_expire(
|
])
|
||||||
task_account_container_list, my_index, divisor)),
|
|
||||||
expected)
|
|
||||||
self.assertEqual(mock_delete_container.mock_calls, [])
|
|
||||||
|
|
||||||
log_lines = self.logger.get_lines_for_level('error')
|
log_lines = self.logger.get_lines_for_level('error')
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
@ -1626,43 +1639,39 @@ class TestObjectExpirer(TestCase):
|
|||||||
% self.just_past_time_container]
|
% self.just_past_time_container]
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
{'tasks.assigned': 5},
|
{'tasks.assigned': 5, 'objects': 5},
|
||||||
self.expirer.logger.statsd_client.get_increment_counts()
|
self.expirer.logger.statsd_client.get_increment_counts()
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_iter_task_to_expire_exception(self):
|
def test_iter_task_to_expire_exception(self):
|
||||||
# Test that object listing on the first container raise Exception, and
|
# Test that object listing on the first container raise Exception, and
|
||||||
# expect the second task container will continue to be processed.
|
# expect the second task container will continue to be processed.
|
||||||
# In this test, all tasks are assigned to the tested expirer.
|
|
||||||
my_index = 0
|
|
||||||
divisor = 1
|
|
||||||
|
|
||||||
# Store reference to the real method before mocking
|
self.expirer.swift.aco_dict['.expiring_objects'][
|
||||||
real_iter_objects = self.fake_swift.iter_objects
|
self.just_past_time_container] = Exception('failed to connect')
|
||||||
|
|
||||||
def mock_iter_objects(account, container, **kwargs):
|
with mock.patch.object(self.expirer, 'pop_queue'):
|
||||||
if container == self.just_past_time_container:
|
self.expirer.run_once()
|
||||||
raise Exception('failed to connect')
|
|
||||||
return real_iter_objects(account, container)
|
|
||||||
|
|
||||||
task_account_container_list = [
|
# everything but the broken container
|
||||||
('.expiring_objects', self.just_past_time_container),
|
expected = sorted(
|
||||||
('.expiring_objects', self.past_time_container)
|
p
|
||||||
]
|
for c, paths in self.expired_target_paths.items()
|
||||||
expected = [
|
for p in paths
|
||||||
self.make_task(self.past_time_container, self.past_time,
|
if c != self.just_past_time
|
||||||
target_path)
|
)
|
||||||
for target_path in self.expired_target_paths[self.past_time]]
|
self.assertEqual(expected, sorted(
|
||||||
|
path
|
||||||
with mock.patch.object(self.expirer.swift, 'iter_objects',
|
for method, path, kwargs in self.expirer.swift._calls
|
||||||
mock_iter_objects):
|
if method == 'delete_object'
|
||||||
with mock.patch.object(self.expirer.swift, 'delete_container') \
|
))
|
||||||
as mock_delete_container:
|
self.assertEqual(
|
||||||
self.assertEqual(
|
[('.expiring_objects/%s' % self.empty_time_container,
|
||||||
list(self.expirer.iter_task_to_expire(
|
{'acceptable_statuses': (2, 404, 409)})], [
|
||||||
task_account_container_list, my_index, divisor)),
|
(path, kwargs)
|
||||||
expected)
|
for method, path, kwargs in self.expirer.swift._calls
|
||||||
self.assertEqual(mock_delete_container.mock_calls, [])
|
if method == 'delete_container'
|
||||||
|
])
|
||||||
|
|
||||||
log_lines = self.logger.get_lines_for_level('error')
|
log_lines = self.logger.get_lines_for_level('error')
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
@ -1672,80 +1681,116 @@ class TestObjectExpirer(TestCase):
|
|||||||
% self.just_past_time_container]
|
% self.just_past_time_container]
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
{'tasks.assigned': 5},
|
{'tasks.assigned': 5, 'objects': 5},
|
||||||
self.expirer.logger.statsd_client.get_increment_counts()
|
self.expirer.logger.statsd_client.get_increment_counts()
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_iter_task_to_expire_404_response_on_empty_container(self):
|
def test_iter_task_to_expire_404_response_on_missing_container(self):
|
||||||
# Test that object listing on an empty container returns 404 and
|
# Test that object listing on a missing container returns 404 and
|
||||||
# raise UnexpectedResponse, and expect ``iter_task_to_expire`` won't
|
# raise UnexpectedResponse, and expect ``iter_task_to_expire`` won't
|
||||||
# delete this task container.
|
# delete this task container.
|
||||||
# In this test, all tasks are assigned to the tested expirer.
|
missing_time = str(self.now - 172800)
|
||||||
my_index = 0
|
missing_time_container = self.get_expirer_container(missing_time)
|
||||||
divisor = 1
|
self.expirer.swift.aco_dict[
|
||||||
|
'.expiring_objects'][missing_time_container] = \
|
||||||
|
internal_client.UnexpectedResponse(
|
||||||
|
'Mocked error', Response(status=404))
|
||||||
|
|
||||||
err_resp = Response(status=404)
|
with mock.patch.object(self.expirer, 'pop_queue'):
|
||||||
err = internal_client.UnexpectedResponse('Mocked error', err_resp)
|
self.expirer.run_once()
|
||||||
|
|
||||||
task_account_container_list = [
|
# all containers iter'd
|
||||||
('.expiring_objects', self.empty_time_container)
|
self.assertEqual([
|
||||||
]
|
('.expiring_objects/%s' % c, {'acceptable_statuses': [2]})
|
||||||
|
for c in [
|
||||||
|
self.empty_time_container,
|
||||||
|
missing_time_container,
|
||||||
|
self.past_time_container,
|
||||||
|
self.just_past_time_container,
|
||||||
|
]
|
||||||
|
], [
|
||||||
|
(path, kwargs) for method, path, kwargs in
|
||||||
|
self.expirer.swift._calls
|
||||||
|
if method == 'iter_objects'
|
||||||
|
])
|
||||||
|
# everything is still expired
|
||||||
|
expected = sorted(
|
||||||
|
p
|
||||||
|
for c, paths in self.expired_target_paths.items()
|
||||||
|
for p in paths
|
||||||
|
)
|
||||||
|
self.assertEqual(expected, sorted(
|
||||||
|
path
|
||||||
|
for method, path, kwargs in self.expirer.swift._calls
|
||||||
|
if method == 'delete_object'
|
||||||
|
))
|
||||||
|
# Only the empty task container gets deleted.
|
||||||
|
self.assertEqual(
|
||||||
|
[('.expiring_objects/%s' % self.empty_time_container,
|
||||||
|
{'acceptable_statuses': (2, 404, 409)})], [
|
||||||
|
(path, kwargs)
|
||||||
|
for method, path, kwargs in self.expirer.swift._calls
|
||||||
|
if method == 'delete_container'
|
||||||
|
])
|
||||||
|
|
||||||
with mock.patch.object(self.expirer.swift, 'iter_objects',
|
|
||||||
side_effect=err) as mock_method:
|
|
||||||
with mock.patch.object(self.expirer.swift, 'delete_container') \
|
|
||||||
as mock_delete_container:
|
|
||||||
self.assertEqual(
|
|
||||||
list(self.expirer.iter_task_to_expire(
|
|
||||||
task_account_container_list, my_index, divisor)),
|
|
||||||
[])
|
|
||||||
log_lines = self.logger.get_lines_for_level('error')
|
log_lines = self.logger.get_lines_for_level('error')
|
||||||
self.assertFalse(log_lines)
|
self.assertFalse(log_lines)
|
||||||
# This empty task container won't get deleted.
|
|
||||||
self.assertEqual(mock_delete_container.mock_calls, [])
|
def test_iter_task_to_expire_503_response_on_container(self):
|
||||||
self.assertEqual(
|
# Test that object listing on a container returns 503 and raise
|
||||||
{}, self.expirer.logger.statsd_client.get_increment_counts())
|
# UnexpectedResponse, and expect ``iter_task_to_expire`` won't delete
|
||||||
self.assertEqual(mock_method.call_args_list, [
|
# this task container.
|
||||||
mock.call('.expiring_objects',
|
missing_time = str(self.now - 172800)
|
||||||
self.empty_time_container,
|
missing_time_container = self.get_expirer_container(missing_time)
|
||||||
acceptable_statuses=[2])
|
self.expirer.swift.aco_dict[
|
||||||
|
'.expiring_objects'][missing_time_container] = \
|
||||||
|
internal_client.UnexpectedResponse(
|
||||||
|
'Mocked error', Response(status=503))
|
||||||
|
|
||||||
|
with mock.patch.object(self.expirer, 'pop_queue'):
|
||||||
|
self.expirer.run_once()
|
||||||
|
|
||||||
|
# all containers iter'd
|
||||||
|
self.assertEqual([
|
||||||
|
('.expiring_objects/%s' % c, {'acceptable_statuses': [2]})
|
||||||
|
for c in [
|
||||||
|
self.empty_time_container,
|
||||||
|
missing_time_container,
|
||||||
|
self.past_time_container,
|
||||||
|
self.just_past_time_container,
|
||||||
|
]
|
||||||
|
], [
|
||||||
|
(path, kwargs) for method, path, kwargs in
|
||||||
|
self.expirer.swift._calls
|
||||||
|
if method == 'iter_objects'
|
||||||
])
|
])
|
||||||
|
# everything is still expired
|
||||||
|
expected = sorted(
|
||||||
|
path
|
||||||
|
for c, paths in self.expired_target_paths.items()
|
||||||
|
for path in paths
|
||||||
|
)
|
||||||
|
self.assertEqual(expected, sorted(
|
||||||
|
path
|
||||||
|
for method, path, kwargs in self.expirer.swift._calls
|
||||||
|
if method == 'delete_object'
|
||||||
|
))
|
||||||
|
# Only the empty task container gets deleted.
|
||||||
|
self.assertEqual(
|
||||||
|
[('.expiring_objects/%s' % self.empty_time_container,
|
||||||
|
{'acceptable_statuses': (2, 404, 409)})], [
|
||||||
|
(path, kwargs)
|
||||||
|
for method, path, kwargs in self.expirer.swift._calls
|
||||||
|
if method == 'delete_container'
|
||||||
|
])
|
||||||
|
|
||||||
def test_iter_task_to_expire_503_response_on_empty_container(self):
|
|
||||||
# Test that object listing on an empty container returns 503 and
|
|
||||||
# raise UnexpectedResponse, and expect ``iter_task_to_expire`` won't
|
|
||||||
# delete this task container.
|
|
||||||
# In this test, all tasks are assigned to the tested expirer.
|
|
||||||
my_index = 0
|
|
||||||
divisor = 1
|
|
||||||
|
|
||||||
def mock_iter_objects(account, container, **kwargs):
|
|
||||||
mock_resp = Response(status=503)
|
|
||||||
raise internal_client.UnexpectedResponse('Mocked error', mock_resp)
|
|
||||||
|
|
||||||
task_account_container_list = [
|
|
||||||
('.expiring_objects', self.empty_time_container)
|
|
||||||
]
|
|
||||||
|
|
||||||
with mock.patch.object(self.expirer.swift, 'iter_objects',
|
|
||||||
side_effect=mock_iter_objects):
|
|
||||||
with mock.patch.object(self.expirer.swift, 'delete_container') \
|
|
||||||
as mock_delete_container:
|
|
||||||
self.assertEqual(
|
|
||||||
list(self.expirer.iter_task_to_expire(
|
|
||||||
task_account_container_list, my_index, divisor)),
|
|
||||||
[])
|
|
||||||
log_lines = self.logger.get_lines_for_level('error')
|
log_lines = self.logger.get_lines_for_level('error')
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
log_lines[0],
|
log_lines[0],
|
||||||
'Unexpected response while listing objects in container '
|
'Unexpected response while listing objects in container '
|
||||||
'.expiring_objects %s: Mocked error'
|
'.expiring_objects %s: Mocked error'
|
||||||
% self.empty_time_container,
|
% missing_time_container,
|
||||||
)
|
)
|
||||||
# This empty task container won't get deleted.
|
|
||||||
self.assertEqual(mock_delete_container.mock_calls, [])
|
|
||||||
self.assertEqual(
|
|
||||||
{}, self.expirer.logger.statsd_client.get_increment_counts())
|
|
||||||
|
|
||||||
def test_run_once_unicode_problem(self):
|
def test_run_once_unicode_problem(self):
|
||||||
requests = []
|
requests = []
|
||||||
|
Loading…
x
Reference in New Issue
Block a user