From b4288b4aa6e6be2222f5f0e9ca8360c07040d5c0 Mon Sep 17 00:00:00 2001 From: Nguyen Quoc Viet Date: Thu, 12 Sep 2019 11:31:42 +0700 Subject: [PATCH] versioned_writes: checks for SLO object before copy Previously, versioned_writes middleware copy an already existing object using PUT. However, SLO requires the additional query to properly update the object size when listing. Propose fix: In _put_versioned_obj - which is called when on creating version obj and also on restoring obj, if 'X-Object-Sysmeta-Slo-Size' header is present it will add needed headers for container to update obj size Added a new functional test case with size assertion for slo Change-Id: I47e0663e67daea8f1cf4eaf3c47e7c8429fd81bc Closes-Bug: #1840322 --- swift/common/middleware/versioned_writes.py | 7 +++- test/functional/test_versioned_writes.py | 41 +++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/swift/common/middleware/versioned_writes.py b/swift/common/middleware/versioned_writes.py index e5f285bed0..1db6a7dee8 100644 --- a/swift/common/middleware/versioned_writes.py +++ b/swift/common/middleware/versioned_writes.py @@ -383,13 +383,18 @@ class VersionedWritesContext(WSGIContext): return source_resp def _put_versioned_obj(self, req, put_path_info, source_resp): - # Create a new Request object to PUT to the versions container, copying + # Create a new Request object to PUT to the container, copying # all headers from the source object apart from x-timestamp. put_req = make_pre_authed_request( req.environ, path=wsgi_quote(put_path_info), method='PUT', swift_source='VW') copy_header_subset(source_resp, put_req, lambda k: k.lower() != 'x-timestamp') + slo_size = put_req.headers.get('X-Object-Sysmeta-Slo-Size') + if slo_size: + put_req.headers['Content-Type'] += '; swift_bytes=' + slo_size + put_req.environ['swift.content_type_overridden'] = True + put_req.environ['wsgi.input'] = FileLikeIter(source_resp.app_iter) put_resp = put_req.get_response(self.app) close_if_possible(source_resp.app_iter) diff --git a/test/functional/test_versioned_writes.py b/test/functional/test_versioned_writes.py index 76573619cc..777a8d3e0d 100644 --- a/test/functional/test_versioned_writes.py +++ b/test/functional/test_versioned_writes.py @@ -1064,3 +1064,44 @@ class TestSloWithVersioning(unittest2.TestCase): # expect the original manifest file to be restored self._assert_is_manifest(file_item, 'a') self._assert_is_object(file_item, b'a') + + def test_slo_manifest_version_size(self): + file_item = self._create_manifest('a') + # sanity check: read the manifest, then the large object + self._assert_is_manifest(file_item, 'a') + self._assert_is_object(file_item, b'a') + + # original manifest size + primary_list = self.container.files(parms={'format': 'json'}) + self.assertEqual(1, len(primary_list)) + org_size = primary_list[0]['bytes'] + + # upload new manifest + file_item = self._create_manifest('b') + # sanity check: read the manifest, then the large object + self._assert_is_manifest(file_item, 'b') + self._assert_is_object(file_item, b'b') + + versions_list = self.versions_container.files(parms={'format': 'json'}) + self.assertEqual(1, len(versions_list)) + version_file = self.versions_container.file(versions_list[0]['name']) + version_file_size = versions_list[0]['bytes'] + # check the version is still a manifest + self._assert_is_manifest(version_file, 'a') + self._assert_is_object(version_file, b'a') + + # check the version size is correct + self.assertEqual(version_file_size, org_size) + + # delete the newest manifest + file_item.delete() + + # expect the original manifest file to be restored + self._assert_is_manifest(file_item, 'a') + self._assert_is_object(file_item, b'a') + + primary_list = self.container.files(parms={'format': 'json'}) + self.assertEqual(1, len(primary_list)) + primary_file_size = primary_list[0]['bytes'] + # expect the original manifest file size to be the same + self.assertEqual(primary_file_size, org_size)