def test_HEAD_x_newest_with_some_missing(self): req = swob.Request.blank('/v1/a/c/o', method='HEAD', headers={'X-Newest': 'true'}) ts = (utils.Timestamp(t) for t in itertools.count(int(time.time()))) request_count = self.app.request_node_count(self.obj_ring.replicas) backend_response_headers = [{ 'x-timestamp': next(ts).normal, } for i in range(request_count)] responses = [404] * (request_count - 1) responses.append(200) request_log = [] def capture_requests(ip, port, device, part, method, path, headers=None, **kwargs): req = { 'ip': ip, 'port': port, 'device': device, 'part': part, 'method': method, 'path': path, 'headers': headers, } request_log.append(req) with set_http_connect(*responses, headers=backend_response_headers, give_connect=capture_requests): resp = req.get_response(self.app) self.assertEqual(resp.status_int, 200) for req in request_log: self.assertEqual(req['method'], 'HEAD') self.assertEqual(req['path'], '/a/c/o')
def test_make_delete_jobs_native_utf8(self): ts = '1558463777.42739' uacct = acct = u'acct-\U0001f334' ucont = cont = u'cont-\N{SNOWMAN}' uobj1 = obj1 = u'obj-\N{GREEK CAPITAL LETTER ALPHA}' uobj2 = obj2 = u'/obj-\N{GREEK CAPITAL LETTER OMEGA}' if six.PY2: acct = acct.encode('utf8') cont = cont.encode('utf8') obj1 = obj1.encode('utf8') obj2 = obj2.encode('utf8') self.assertEqual( container_deleter.make_delete_jobs(acct, cont, [obj1, obj2], utils.Timestamp(ts)), [{ 'name': u'%s-%s/%s/%s' % (ts, uacct, ucont, uobj1), 'deleted': 0, 'created_at': ts, 'etag': utils.MD5_OF_EMPTY_STRING, 'size': 0, 'storage_policy_index': 0, 'content_type': 'application/async-deleted' }, { 'name': u'%s-%s/%s/%s' % (ts, uacct, ucont, uobj2), 'deleted': 0, 'created_at': ts, 'etag': utils.MD5_OF_EMPTY_STRING, 'size': 0, 'storage_policy_index': 0, 'content_type': 'application/async-deleted' }])
def test_make_delete_jobs_unicode_utf8(self): ts = '1558463777.42739' acct = u'acct-\U0001f334' cont = u'cont-\N{SNOWMAN}' obj1 = u'obj-\N{GREEK CAPITAL LETTER ALPHA}' obj2 = u'obj-\N{GREEK CAPITAL LETTER OMEGA}' self.assertEqual( container_deleter.make_delete_jobs(acct, cont, [obj1, obj2], utils.Timestamp(ts)), [{ 'name': u'%s-%s/%s/%s' % (ts.split('.')[0], acct, cont, obj1), 'deleted': 0, 'created_at': ts, 'etag': utils.MD5_OF_EMPTY_STRING, 'size': 0, 'storage_policy_index': 0, 'content_type': 'application/async-deleted' }, { 'name': u'%s-%s/%s/%s' % (ts.split('.')[0], acct, cont, obj2), 'deleted': 0, 'created_at': ts, 'etag': utils.MD5_OF_EMPTY_STRING, 'size': 0, 'storage_policy_index': 0, 'content_type': 'application/async-deleted' }])
def test_mark_for_deletion_one_update_no_yield(self): ts = '1558463777.42739' with FakeInternalClient([ swob.Response( json.dumps([ { 'name': '/obj1' }, { 'name': 'obj2' }, { 'name': 'obj3' }, ])), swob.Response(json.dumps([])), swob.Response(status=202), ]) as swift: self.assertEqual( container_deleter.mark_for_deletion( swift, 'account', 'container', '', '', '', timestamp=utils.Timestamp(ts), yield_time=None, ), 3) self.assertEqual(swift.calls, [ ('GET', '/v1/account/container', 'format=json&marker=&end_marker=&prefix=', {}, None), ('GET', '/v1/account/container', 'format=json&marker=obj3&end_marker=&prefix=', {}, None), ('UPDATE', '/v1/.expiring_objects/' + ts.split('.')[0], '', { 'X-Backend-Allow-Private-Methods': 'True', 'X-Backend-Storage-Policy-Index': '0', 'X-Timestamp': ts }, mock.ANY), ]) self.assertEqual( json.loads(swift.calls[-1].body), container_deleter.make_delete_jobs('account', 'container', ['/obj1', 'obj2', 'obj3'], utils.Timestamp(ts)))
def test_send_delete(self): self.sender.connection = FakeConnection() self.sender.send_delete('/a/c/o', utils.Timestamp('1381679759.90941')) self.assertEqual( ''.join(self.sender.connection.sent), '30\r\n' 'DELETE /a/c/o\r\n' 'X-Timestamp: 1381679759.90941\r\n' '\r\n\r\n')
def test_valid_timestamp(self): self.assertRaises(HTTPException, constraints.valid_timestamp, Request.blank('/')) self.assertRaises(HTTPException, constraints.valid_timestamp, Request.blank('/', headers={'X-Timestamp': 'asdf'})) timestamp = utils.Timestamp(time.time()) req = Request.blank('/', headers={'X-Timestamp': timestamp.internal}) self.assertEqual(timestamp, constraints.valid_timestamp(req)) req = Request.blank('/', headers={'X-Timestamp': timestamp.normal}) self.assertEqual(timestamp, constraints.valid_timestamp(req))
def test_send_delete_timeout(self): self.sender.connection = FakeConnection() self.sender.connection.send = lambda d: eventlet.sleep(1) self.sender.daemon.node_timeout = 0.01 exc = None try: self.sender.send_delete('/a/c/o', utils.Timestamp('1381679759.90941')) except exceptions.MessageTimeout as err: exc = err self.assertEqual(str(exc), '0.01 seconds: send_delete')
def test_container_sync_delete(self): ts = (utils.Timestamp(t) for t in itertools.count(int(time.time()))) test_indexes = [None] + [int(p) for p in POLICIES] for policy_index in test_indexes: req = swob.Request.blank( '/v1/a/c/o', method='DELETE', headers={ 'X-Timestamp': ts.next().internal}) codes = [409] * self.obj_ring.replicas ts_iter = itertools.repeat(ts.next().internal) with set_http_connect(*codes, timestamps=ts_iter): resp = req.get_response(self.app) self.assertEqual(resp.status_int, 409)
def test_put_x_timestamp_conflict(self): ts = (utils.Timestamp(t) for t in itertools.count(int(time.time()))) req = swob.Request.blank( '/v1/a/c/o', method='PUT', headers={ 'Content-Length': 0, 'X-Timestamp': ts.next().internal}) head_resp = [404] * self.obj_ring.replicas + \ [404] * self.obj_ring.max_more_nodes put_resp = [409] + [201] * self.obj_ring.replicas codes = head_resp + put_resp with set_http_connect(*codes): resp = req.get_response(self.app) self.assertEqual(resp.status_int, 201)
def test_put_x_timestamp_conflict(self): ts = (utils.Timestamp(t) for t in itertools.count(int(time.time()))) req = swob.Request.blank('/v1/a/c/o', method='PUT', headers={ 'Content-Length': 0, 'X-Timestamp': ts.next().internal }) ts_iter = iter([ts.next().internal, None, None]) codes = [409] + [201] * (self.obj_ring.replicas - 1) with set_http_connect(*codes, timestamps=ts_iter): resp = req.get_response(self.app) self.assertEqual(resp.status_int, 202)
def make_ec_object_stub(test_body, policy, timestamp): segment_size = policy.ec_segment_size test_body = test_body or ('test' * segment_size)[:-random.randint(1, 1000)] timestamp = timestamp or utils.Timestamp(time.time()) etag = md5(test_body).hexdigest() ec_archive_bodies = encode_frag_archive_bodies(policy, test_body) return { 'body': test_body, 'etag': etag, 'frags': ec_archive_bodies, 'timestamp': timestamp }
def test_container_sync_put_x_timestamp_not_found(self): test_indexes = [None] + [int(p) for p in POLICIES] for policy_index in test_indexes: self.container_info['storage_policy'] = policy_index put_timestamp = utils.Timestamp(time.time()).normal req = swob.Request.blank('/v1/a/c/o', method='PUT', headers={ 'Content-Length': 0, 'X-Timestamp': put_timestamp }) codes = [201] * self.obj_ring.replicas with set_http_connect(*codes): resp = req.get_response(self.app) self.assertEqual(resp.status_int, 201)
def test_container_sync_put_x_timestamp_newer(self): ts = (utils.Timestamp(t) for t in itertools.count(int(time.time()))) test_indexes = [None] + [int(p) for p in POLICIES] for policy_index in test_indexes: orig_timestamp = ts.next().internal req = swob.Request.blank('/v1/a/c/o', method='PUT', headers={ 'Content-Length': 0, 'X-Timestamp': ts.next().internal }) ts_iter = itertools.repeat(orig_timestamp) codes = [201] * self.obj_ring.replicas with set_http_connect(*codes, timestamps=ts_iter): resp = req.get_response(self.app) self.assertEqual(resp.status_int, 201)
def test_HEAD_x_newest_different_timestamps(self): req = swob.Request.blank('/v1/a/c/o', method='HEAD', headers={'X-Newest': 'true'}) ts = (utils.Timestamp(t) for t in itertools.count(int(time.time()))) timestamps = [next(ts) for i in range(3)] newest_timestamp = timestamps[-1] random.shuffle(timestamps) backend_response_headers = [{ 'X-Backend-Timestamp': t.internal, 'X-Timestamp': t.normal } for t in timestamps] with set_http_connect(200, 200, 200, headers=backend_response_headers): resp = req.get_response(self.app) self.assertEqual(resp.status_int, 200) self.assertEqual(resp.headers['x-timestamp'], newest_timestamp.normal)
def test_container_sync_put_x_timestamp_older(self): ts = (utils.Timestamp(t) for t in itertools.count(int(time.time()))) test_indexes = [None] + [int(p) for p in POLICIES] for policy_index in test_indexes: self.container_info['storage_policy'] = policy_index req = swob.Request.blank( '/v1/a/c/o', method='PUT', headers={ 'Content-Length': 0, 'X-Timestamp': ts.next().internal}) ts_iter = itertools.repeat(ts.next().internal) head_resp = [200] * self.obj_ring.replicas + \ [404] * self.obj_ring.max_more_nodes codes = head_resp with set_http_connect(*codes, timestamps=ts_iter): resp = req.get_response(self.app) self.assertEqual(resp.status_int, 202)
def test_container_sync_put_x_timestamp_race(self): ts = (utils.Timestamp(t) for t in itertools.count(int(time.time()))) test_indexes = [None] + [int(p) for p in POLICIES] for policy_index in test_indexes: put_timestamp = ts.next().internal req = swob.Request.blank('/v1/a/c/o', method='PUT', headers={ 'Content-Length': 0, 'X-Timestamp': put_timestamp }) # object nodes they respond 409 because another in-flight request # finished and now the on disk timestamp is equal to the request. put_ts = [put_timestamp] * self.obj_ring.replicas codes = [409] * self.obj_ring.replicas ts_iter = iter(put_ts) with set_http_connect(*codes, timestamps=ts_iter): resp = req.get_response(self.app) self.assertEqual(resp.status_int, 202)
def test_make_delete_jobs(self): ts = '1558463777.42739' self.assertEqual( container_deleter.make_delete_jobs('acct', 'cont', ['obj1', 'obj2'], utils.Timestamp(ts)), [{ 'name': ts + '-acct/cont/obj1', 'deleted': 0, 'created_at': ts, 'etag': utils.MD5_OF_EMPTY_STRING, 'size': 0, 'storage_policy_index': 0, 'content_type': 'application/async-deleted' }, { 'name': ts + '-acct/cont/obj2', 'deleted': 0, 'created_at': ts, 'etag': utils.MD5_OF_EMPTY_STRING, 'size': 0, 'storage_policy_index': 0, 'content_type': 'application/async-deleted' }])
def test_container_sync_put_x_timestamp_unsynced_race(self): ts = (utils.Timestamp(t) for t in itertools.count(int(time.time()))) test_indexes = [None] + [int(p) for p in POLICIES] for policy_index in test_indexes: put_timestamp = ts.next().internal req = swob.Request.blank('/v1/a/c/o', method='PUT', headers={ 'Content-Length': 0, 'X-Timestamp': put_timestamp }) # only one in-flight request finished put_ts = [None] * (self.obj_ring.replicas - 1) put_resp = [201] * (self.obj_ring.replicas - 1) put_ts += [put_timestamp] put_resp += [409] ts_iter = iter(put_ts) codes = put_resp with set_http_connect(*codes, timestamps=ts_iter): resp = req.get_response(self.app) self.assertEqual(resp.status_int, 202)
def test_check_delete_headers(self): # x-delete-at value should be relative to the request timestamp rather # than time.time() so separate the two to ensure the checks are robust ts = utils.Timestamp(time.time() + 100) # X-Delete-After headers = {'X-Delete-After': '600', 'X-Timestamp': ts.internal} req = constraints.check_delete_headers( Request.blank('/', headers=headers)) self.assertIsInstance(req, Request) self.assertIn('x-delete-at', req.headers) self.assertNotIn('x-delete-after', req.headers) expected_delete_at = str(int(ts) + 600) self.assertEqual(req.headers.get('X-Delete-At'), expected_delete_at) headers = {'X-Delete-After': 'abc', 'X-Timestamp': ts.internal} with self.assertRaises(HTTPException) as cm: constraints.check_delete_headers( Request.blank('/', headers=headers)) self.assertEqual(cm.exception.status_int, HTTP_BAD_REQUEST) self.assertIn(b'Non-integer X-Delete-After', cm.exception.body) headers = {'X-Delete-After': '60.1', 'X-Timestamp': ts.internal} with self.assertRaises(HTTPException) as cm: constraints.check_delete_headers( Request.blank('/', headers=headers)) self.assertEqual(cm.exception.status_int, HTTP_BAD_REQUEST) self.assertIn(b'Non-integer X-Delete-After', cm.exception.body) headers = {'X-Delete-After': '-1', 'X-Timestamp': ts.internal} with self.assertRaises(HTTPException) as cm: constraints.check_delete_headers( Request.blank('/', headers=headers)) self.assertEqual(cm.exception.status_int, HTTP_BAD_REQUEST) self.assertIn(b'X-Delete-After in past', cm.exception.body) headers = {'X-Delete-After': '0', 'X-Timestamp': ts.internal} with self.assertRaises(HTTPException) as cm: constraints.check_delete_headers( Request.blank('/', headers=headers)) self.assertEqual(cm.exception.status_int, HTTP_BAD_REQUEST) self.assertIn(b'X-Delete-After in past', cm.exception.body) # x-delete-after = 0 disallowed when it results in x-delete-at equal to # the timestamp headers = { 'X-Delete-After': '0', 'X-Timestamp': utils.Timestamp(int(ts)).internal } with self.assertRaises(HTTPException) as cm: constraints.check_delete_headers( Request.blank('/', headers=headers)) self.assertEqual(cm.exception.status_int, HTTP_BAD_REQUEST) self.assertIn(b'X-Delete-After in past', cm.exception.body) # X-Delete-At delete_at = str(int(ts) + 100) headers = {'X-Delete-At': delete_at, 'X-Timestamp': ts.internal} req = constraints.check_delete_headers( Request.blank('/', headers=headers)) self.assertIsInstance(req, Request) self.assertIn('x-delete-at', req.headers) self.assertEqual(req.headers.get('X-Delete-At'), delete_at) headers = {'X-Delete-At': 'abc', 'X-Timestamp': ts.internal} with self.assertRaises(HTTPException) as cm: constraints.check_delete_headers( Request.blank('/', headers=headers)) self.assertEqual(cm.exception.status_int, HTTP_BAD_REQUEST) self.assertIn(b'Non-integer X-Delete-At', cm.exception.body) delete_at = str(int(ts) + 100) + '.1' headers = {'X-Delete-At': delete_at, 'X-Timestamp': ts.internal} with self.assertRaises(HTTPException) as cm: constraints.check_delete_headers( Request.blank('/', headers=headers)) self.assertEqual(cm.exception.status_int, HTTP_BAD_REQUEST) self.assertIn(b'Non-integer X-Delete-At', cm.exception.body) delete_at = str(int(ts) - 1) headers = {'X-Delete-At': delete_at, 'X-Timestamp': ts.internal} with self.assertRaises(HTTPException) as cm: constraints.check_delete_headers( Request.blank('/', headers=headers)) self.assertEqual(cm.exception.status_int, HTTP_BAD_REQUEST) self.assertIn(b'X-Delete-At in past', cm.exception.body) # x-delete-at disallowed when exactly equal to timestamp delete_at = str(int(ts)) headers = { 'X-Delete-At': delete_at, 'X-Timestamp': utils.Timestamp(int(ts)).internal } with self.assertRaises(HTTPException) as cm: constraints.check_delete_headers( Request.blank('/', headers=headers)) self.assertEqual(cm.exception.status_int, HTTP_BAD_REQUEST) self.assertIn(b'X-Delete-At in past', cm.exception.body)
def test_print_obj_metadata(self): self.assertRaisesMessage(ValueError, 'Metadata is None', print_obj_metadata, []) def get_metadata(items): md = dict(name='/AUTH_admin/c/dummy') md['Content-Type'] = 'application/octet-stream' md['X-Timestamp'] = 106.3 md.update(items) return md metadata = get_metadata({'X-Object-Meta-Mtime': '107.3'}) out = StringIO() with mock.patch('sys.stdout', out): print_obj_metadata(metadata) exp_out = '''Path: /AUTH_admin/c/dummy Account: AUTH_admin Container: c Object: dummy Object hash: 128fdf98bddd1b1e8695f4340e67a67a Content-Type: application/octet-stream Timestamp: 1970-01-01T00:01:46.300000 (%s) System Metadata: No metadata found User Metadata: X-Object-Meta-Mtime: 107.3 Other Metadata: No metadata found''' % (utils.Timestamp(106.3).internal) self.assertEqual(out.getvalue().strip(), exp_out) metadata = get_metadata({ 'X-Object-Sysmeta-Mtime': '107.3', 'X-Object-Sysmeta-Name': 'Obj name', }) out = StringIO() with mock.patch('sys.stdout', out): print_obj_metadata(metadata) exp_out = '''Path: /AUTH_admin/c/dummy Account: AUTH_admin Container: c Object: dummy Object hash: 128fdf98bddd1b1e8695f4340e67a67a Content-Type: application/octet-stream Timestamp: 1970-01-01T00:01:46.300000 (%s) System Metadata: X-Object-Sysmeta-Mtime: 107.3 X-Object-Sysmeta-Name: Obj name User Metadata: No metadata found Other Metadata: No metadata found''' % (utils.Timestamp(106.3).internal) self.assertEqual(out.getvalue().strip(), exp_out) metadata = get_metadata({ 'X-Object-Meta-Mtime': '107.3', 'X-Object-Sysmeta-Mtime': '107.3', 'X-Object-Mtime': '107.3', }) out = StringIO() with mock.patch('sys.stdout', out): print_obj_metadata(metadata) exp_out = '''Path: /AUTH_admin/c/dummy Account: AUTH_admin Container: c Object: dummy Object hash: 128fdf98bddd1b1e8695f4340e67a67a Content-Type: application/octet-stream Timestamp: 1970-01-01T00:01:46.300000 (%s) System Metadata: X-Object-Sysmeta-Mtime: 107.3 User Metadata: X-Object-Meta-Mtime: 107.3 Other Metadata: X-Object-Mtime: 107.3''' % (utils.Timestamp(106.3).internal) self.assertEqual(out.getvalue().strip(), exp_out) metadata = get_metadata({}) out = StringIO() with mock.patch('sys.stdout', out): print_obj_metadata(metadata) exp_out = '''Path: /AUTH_admin/c/dummy Account: AUTH_admin Container: c Object: dummy Object hash: 128fdf98bddd1b1e8695f4340e67a67a Content-Type: application/octet-stream Timestamp: 1970-01-01T00:01:46.300000 (%s) System Metadata: No metadata found User Metadata: No metadata found Other Metadata: No metadata found''' % (utils.Timestamp(106.3).internal) self.assertEqual(out.getvalue().strip(), exp_out) metadata = get_metadata({'X-Object-Meta-Mtime': '107.3'}) metadata['name'] = '/a-s' self.assertRaisesMessage(ValueError, 'Path is invalid', print_obj_metadata, metadata) metadata = get_metadata({'X-Object-Meta-Mtime': '107.3'}) del metadata['name'] out = StringIO() with mock.patch('sys.stdout', out): print_obj_metadata(metadata) exp_out = '''Path: Not found in metadata Content-Type: application/octet-stream Timestamp: 1970-01-01T00:01:46.300000 (%s) System Metadata: No metadata found User Metadata: X-Object-Meta-Mtime: 107.3 Other Metadata: No metadata found''' % (utils.Timestamp(106.3).internal) self.assertEqual(out.getvalue().strip(), exp_out) metadata = get_metadata({'X-Object-Meta-Mtime': '107.3'}) del metadata['Content-Type'] out = StringIO() with mock.patch('sys.stdout', out): print_obj_metadata(metadata) exp_out = '''Path: /AUTH_admin/c/dummy Account: AUTH_admin Container: c Object: dummy Object hash: 128fdf98bddd1b1e8695f4340e67a67a Content-Type: Not found in metadata Timestamp: 1970-01-01T00:01:46.300000 (%s) System Metadata: No metadata found User Metadata: X-Object-Meta-Mtime: 107.3 Other Metadata: No metadata found''' % (utils.Timestamp(106.3).internal) self.assertEqual(out.getvalue().strip(), exp_out) metadata = get_metadata({'X-Object-Meta-Mtime': '107.3'}) del metadata['X-Timestamp'] out = StringIO() with mock.patch('sys.stdout', out): print_obj_metadata(metadata) exp_out = '''Path: /AUTH_admin/c/dummy Account: AUTH_admin Container: c Object: dummy Object hash: 128fdf98bddd1b1e8695f4340e67a67a Content-Type: application/octet-stream Timestamp: Not found in metadata System Metadata: No metadata found User Metadata: X-Object-Meta-Mtime: 107.3 Other Metadata: No metadata found''' self.assertEqual(out.getvalue().strip(), exp_out)