Merge "py3: port expirer"

This commit is contained in:
Zuul 2019-05-22 05:59:41 +00:00 committed by Gerrit Code Review
commit 720979e3f1
3 changed files with 31 additions and 27 deletions

View File

@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from six.moves import urllib import six
from random import random from random import random
from time import time from time import time
@ -31,6 +31,7 @@ from swift.common.utils import get_logger, dump_recon_cache, split_path, \
Timestamp, config_true_value Timestamp, config_true_value
from swift.common.http import HTTP_NOT_FOUND, HTTP_CONFLICT, \ from swift.common.http import HTTP_NOT_FOUND, HTTP_CONFLICT, \
HTTP_PRECONDITION_FAILED HTTP_PRECONDITION_FAILED
from swift.common.swob import wsgi_quote, str_to_wsgi
from swift.container.reconciler import direct_delete_container_entry from swift.container.reconciler import direct_delete_container_entry
@ -182,6 +183,8 @@ class ObjectExpirer(Daemon):
:param divisor: a divisor number :param divisor: a divisor number
:return: an integer to decide which expirer is assigned to the task :return: an integer to decide which expirer is assigned to the task
""" """
if not isinstance(name, bytes):
name = name.encode('utf8')
# md5 is only used for shuffling mod # md5 is only used for shuffling mod
return int(hashlib.md5(name).hexdigest(), 16) % divisor return int(hashlib.md5(name).hexdigest(), 16) % divisor
@ -229,7 +232,10 @@ class ObjectExpirer(Daemon):
""" """
for task_account, task_container in task_account_container_list: for task_account, task_container in task_account_container_list:
for o in self.swift.iter_objects(task_account, task_container): for o in self.swift.iter_objects(task_account, task_container):
task_object = o['name'].encode('utf8') if six.PY2:
task_object = o['name'].encode('utf8')
else:
task_object = o['name']
try: try:
delete_timestamp, target_account, target_container, \ delete_timestamp, target_account, target_container, \
target_object = self.parse_task_obj(task_object) target_object = self.parse_task_obj(task_object)
@ -439,7 +445,7 @@ class ObjectExpirer(Daemon):
:raises UnexpectedResponse: if the delete was unsuccessful and :raises UnexpectedResponse: if the delete was unsuccessful and
should be retried later should be retried later
""" """
path = '/v1/' + urllib.parse.quote(actual_obj.lstrip('/')) path = '/v1/' + wsgi_quote(str_to_wsgi(actual_obj.lstrip('/')))
self.swift.make_request( self.swift.make_request(
'DELETE', path, 'DELETE', path,
{'X-If-Delete-At': timestamp.normal, {'X-If-Delete-At': timestamp.normal,

View File

@ -69,8 +69,9 @@ class FakeInternalClient(object):
def iter_containers(self, account, prefix=''): def iter_containers(self, account, prefix=''):
acc_dict = self.aco_dict[account] acc_dict = self.aco_dict[account]
return sorted([{'name': six.text_type(container)} for container in return [{'name': six.text_type(container)}
acc_dict if container.startswith(prefix)]) for container in sorted(acc_dict)
if container.startswith(prefix)]
def delete_container(*a, **kw): def delete_container(*a, **kw):
pass pass
@ -131,9 +132,11 @@ class TestObjectExpirer(TestCase):
# target object paths which should be expirerd now # target object paths which should be expirerd now
self.expired_target_path_list = [ self.expired_target_path_list = [
'a0/c0/o0', 'a1/c1/o1', 'a2/c2/o2', 'a3/c3/o3', 'a4/c4/o4', swob.wsgi_to_str(tgt) for tgt in (
'a5/c5/o5', 'a6/c6/o6', 'a7/c7/o7', 'a0/c0/o0', 'a1/c1/o1', 'a2/c2/o2', 'a3/c3/o3', 'a4/c4/o4',
'a8/c8/o8\xe2\x99\xa1', 'a9/c9/o9\xc3\xb8', 'a5/c5/o5', 'a6/c6/o6', 'a7/c7/o7',
'a8/c8/o8\xe2\x99\xa1', 'a9/c9/o9\xc3\xb8',
)
] ]
def tearDown(self): def tearDown(self):
@ -656,23 +659,23 @@ class TestObjectExpirer(TestCase):
self.expirer.run_once() self.expirer.run_once()
self.assertEqual(self.expirer.report_objects, 10) self.assertEqual(self.expirer.report_objects, 10)
def test_delete_actual_object_does_not_get_unicode(self): def test_delete_actual_object_gets_native_string(self):
got_unicode = [False] got_str = [False]
def delete_actual_object_test_for_unicode(actual_obj, timestamp): def delete_actual_object_test_for_string(actual_obj, timestamp):
if isinstance(actual_obj, six.text_type): if isinstance(actual_obj, str):
got_unicode[0] = True got_str[0] = True
self.assertEqual(self.expirer.report_objects, 0) self.assertEqual(self.expirer.report_objects, 0)
with mock.patch.object(self.expirer, 'delete_actual_object', with mock.patch.object(self.expirer, 'delete_actual_object',
delete_actual_object_test_for_unicode), \ delete_actual_object_test_for_string), \
mock.patch.object(self.expirer, 'pop_queue', mock.patch.object(self.expirer, 'pop_queue',
lambda a, c, o: None): lambda a, c, o: None):
self.expirer.run_once() self.expirer.run_once()
self.assertEqual(self.expirer.report_objects, 10) self.assertEqual(self.expirer.report_objects, 10)
self.assertFalse(got_unicode[0]) self.assertTrue(got_str[0])
def test_failed_delete_continues_on(self): def test_failed_delete_continues_on(self):
def fail_delete_container(*a, **kw): def fail_delete_container(*a, **kw):
@ -713,19 +716,12 @@ class TestObjectExpirer(TestCase):
interval = 1234 interval = 1234
x = expirer.ObjectExpirer({'__file__': 'unit_test', x = expirer.ObjectExpirer({'__file__': 'unit_test',
'interval': interval}) 'interval': interval})
orig_random = expirer.random with mock.patch.object(expirer, 'random', not_random), \
orig_sleep = expirer.sleep mock.patch.object(expirer, 'sleep', not_sleep), \
try: self.assertRaises(SystemExit) as caught:
expirer.random = not_random
expirer.sleep = not_sleep
x.run_once = raise_system_exit x.run_once = raise_system_exit
x.run_forever() x.run_forever()
except SystemExit as err: self.assertEqual(str(caught.exception), 'test_run_forever')
pass
finally:
expirer.random = orig_random
expirer.sleep = orig_sleep
self.assertEqual(str(err), 'test_run_forever')
self.assertEqual(last_not_sleep, 0.5 * interval) self.assertEqual(last_not_sleep, 0.5 * interval)
def test_run_forever_catches_usual_exceptions(self): def test_run_forever_catches_usual_exceptions(self):
@ -872,7 +868,8 @@ class TestObjectExpirer(TestCase):
with mocked_http_conn( with mocked_http_conn(
200, 200, 200, give_connect=capture_requests) as fake_conn: 200, 200, 200, give_connect=capture_requests) as fake_conn:
x.pop_queue('a', 'c', 'o') x.pop_queue('a', 'c', 'o')
self.assertRaises(StopIteration, fake_conn.code_iter.next) with self.assertRaises(StopIteration):
next(fake_conn.code_iter)
for method, path in requests: for method, path in requests:
self.assertEqual(method, 'DELETE') self.assertEqual(method, 'DELETE')
device, part, account, container, obj = utils.split_path( device, part, account, container, obj = utils.split_path(

View File

@ -94,6 +94,7 @@ commands =
test/unit/common/test_wsgi.py \ test/unit/common/test_wsgi.py \
test/unit/container \ test/unit/container \
test/unit/obj/test_auditor.py \ test/unit/obj/test_auditor.py \
test/unit/obj/test_expirer.py \
test/unit/obj/test_replicator.py \ test/unit/obj/test_replicator.py \
test/unit/obj/test_server.py \ test/unit/obj/test_server.py \
test/unit/obj/test_updater.py \ test/unit/obj/test_updater.py \