diff --git a/test/unit/proxy/test_server.py b/test/unit/proxy/test_server.py index 4081f3a..ba5f2f6 100644 --- a/test/unit/proxy/test_server.py +++ b/test/unit/proxy/test_server.py @@ -39,9 +39,11 @@ import functools from swift.obj import diskfile import re import random +from collections import defaultdict import mock from eventlet import sleep, spawn, wsgi, listen, Timeout +from eventlet.green import httplib from six import BytesIO from six import StringIO from six.moves import range @@ -6072,6 +6074,100 @@ class TestECMismatchedFA(unittest.TestCase): self.assertEqual(resp.status_int, 503) +class TestObjectDisconnectCleanup(unittest.TestCase): + + # update this if you need to make more different devices in do_setup + device_pattern = re.compile('sd[a-z][0-9]') + + def tearDown(self): + # make sure all the object data is cleaned up + for dev in os.listdir(_testdir): + if not self.device_pattern.match(dev): + continue + device_path = os.path.join(_testdir, dev) + for datadir in os.listdir(device_path): + if 'object' not in datadir: + continue + data_path = os.path.join(device_path, datadir) + rmtree(data_path, ignore_errors=True) + mkdirs(data_path) + found_files = self.find_files() + self.assertEqual(found_files['.data'], []) + + def _check_disconnect_cleans_up(self, policy_name): + proxy_port = _test_sockets[0].getsockname()[1] + + def put(path, headers=None, body=None): + conn = httplib.HTTPConnection('localhost', proxy_port) + try: + conn.connect() + conn.putrequest('PUT', path) + for k, v in (headers or {}).items(): + conn.putheader(k, v) + conn.endheaders() + body = body or [''] + for chunk in body: + conn.send(chunk) + resp = conn.getresponse() + body = resp.read() + finally: + # seriously - shut this mother down + if conn.sock: + conn.sock.fd.close() + return resp, body + + # ensure container + container_path = '/v1/a/%s-disconnect-test' % policy_name + resp, _body = put(container_path, headers={ + 'Connection': 'close', + 'X-Storage-Policy': policy_name, + 'Content-Length': '0', + }) + self.assertIn(resp.status, (201, 202)) + + def exploding_body(): + for i in range(3): + yield '\x00' * (64 * 2 ** 10) + raise Exception('kaboom!') + + obj_path = container_path + '/disconnect-data' + try: + resp, _body = put(obj_path, headers={ + 'Content-Length': 64 * 2 ** 20, + }, body=exploding_body()) + except Exception as e: + if str(e) != 'kaboom!': + raise + else: + self.fail('obj put connection did not ka-splod') + + # give everyone queue's a chance to realize socket is closed + # anything > 8 seems to WOMM - but I didn't bother to count up + # everyone's trampolines or anything... + for coro in range(3 * len(_test_coros)): + sleep(0) + + def find_files(self): + found_files = defaultdict(list) + for root, dirs, files in os.walk(_testdir): + for fname in files: + filename, ext = os.path.splitext(fname) + found_files[ext].append(os.path.join(root, fname)) + return found_files + + def test_repl_disconnect_cleans_up(self): + self._check_disconnect_cleans_up('zero') + found_files = self.find_files() + self.assertEqual(found_files['.data'], []) + + def test_ec_disconnect_cleans_up(self): + print('*' * 50, 'doing it') + self._check_disconnect_cleans_up('ec') + print('*' * 50, 'done') + found_files = self.find_files() + self.assertEqual(found_files['.data'], []) + + class TestObjectECRangedGET(unittest.TestCase): def setUp(self): _test_servers[0].logger._clear()