diff --git a/doc/source/api/large_objects.rst b/doc/source/api/large_objects.rst index c84b53e2f8..aafdc9507f 100644 --- a/doc/source/api/large_objects.rst +++ b/doc/source/api/large_objects.rst @@ -28,11 +28,12 @@ object. The large object is comprised of two types of objects: the segment objects in JSON format. **Dynamic large objects** - The manifest object has no content but it has a - ``X-Object-Manifest`` metadata header. The value of this header - is ``{container}/{prefix}``, where ``{container}`` is the name of - the container where the segment objects are stored, and - ``{prefix}`` is a string that all segment objects have in common. + The manifest object has a ``X-Object-Manifest`` metadata header. + The value of this header is ``{container}/{prefix}``, + where ``{container}`` is the name of the container where the + segment objects are stored, and ``{prefix}`` is a string that all + segment objects have in common. The manifest object should have + no content. However, this is not enforced. Note ~~~~ @@ -288,8 +289,14 @@ both the original and new manifest objects share the same set of segment objects. When creating dynamic large objects, the **COPY** operation does not create -a manifest object. To duplicate a manifest object, use the **GET** operation -to read the value of ``X-Object-Manifest`` and use this value in the -``X-Object-Manifest`` request header in a **PUT** operation. This creates -a new manifest object that shares the same set of segment objects as the -original manifest object. +a manifest object but a normal object with content same as what you would +get on a **GET** request to original manifest object. + +To duplicate a manifest object: +* Use the **GET** operation to read the value of ``X-Object-Manifest`` and + use this value in the ``X-Object-Manifest`` request header in a **PUT** + operation. +* Alternatively, you can include *``?multipart-manifest=get``* query + string in the **COPY** request. +This creates a new manifest object that shares the same set of segment +objects as the original manifest object. diff --git a/doc/source/overview_large_objects.rst b/doc/source/overview_large_objects.rst index 03ed8aed35..1d5f1913a4 100644 --- a/doc/source/overview_large_objects.rst +++ b/doc/source/overview_large_objects.rst @@ -40,7 +40,7 @@ So now, the following ``swift`` command would download the entire large object:: swift download test_container large_file -``swift`` uses a strict convention for its segmented object +``swift`` command uses a strict convention for its segmented object support. In the above example it will upload all the segments into a second container named test_container_segments. These segments will have names like large_file/1290206778.25/21474836480/00000000, @@ -66,14 +66,15 @@ Direct API You can also work with the segments and manifests directly with HTTP requests instead of having ``swift`` do that for you. You can just upload the segments like you would any other object and the manifest -is just a zero-byte file with an extra ``X-Object-Manifest`` header. +is just a zero-byte (not enforced) file with an extra +``X-Object-Manifest`` header. All the object segments need to be in the same container, have a common object name prefix, and their names sort in the order they should be concatenated. They don't have to be in the same container as the manifest file will be, which is useful to keep container listings clean as explained above with ``swift``. -The manifest file is simply a zero-byte file with the extra +The manifest file is simply a zero-byte (not enforced) file with the extra ``X-Object-Manifest: /`` header, where ```` is the container the object segments are in and ```` is the common prefix for all the segments. @@ -85,6 +86,17 @@ location and then update the manifest to point to this new location. During the upload of the new segments, the original manifest will still be available to download the first set of segments. +.. note:: + + The manifest file should have no content. However, this is not enforced. + If the manifest path itself conforms to container/prefix specified in + X-Object-Manifest, and if manifest has some content/data in it, it would + also be considered as segment and manifest's content will be part of the + concatenated GET response. The order of concatenation follows the usual DLO + logic which is - the order of concatenation adheres to order returned when + segment names are sorted. + + Here's an example using ``curl`` with tiny 1-byte segments:: # First, upload the segments diff --git a/test/functional/swift_test_client.py b/test/functional/swift_test_client.py index 941dfbbf73..7148562cfd 100644 --- a/test/functional/swift_test_client.py +++ b/test/functional/swift_test_client.py @@ -722,8 +722,10 @@ class File(Base): ['content_type', 'content-type'], ['last_modified', 'last-modified'], ['etag', 'etag']] + optional_fields = [['x_object_manifest', 'x-object-manifest']] - header_fields = self.header_fields(fields) + header_fields = self.header_fields(fields, + optional_fields=optional_fields) header_fields['etag'] = header_fields['etag'].strip('"') return header_fields diff --git a/test/functional/tests.py b/test/functional/tests.py index 546e578352..9274f75a1c 100644 --- a/test/functional/tests.py +++ b/test/functional/tests.py @@ -1805,6 +1805,9 @@ class TestDlo(Base): file_item = self.env.container.file('man1') file_contents = file_item.read(parms={'multipart-manifest': 'get'}) self.assertEqual(file_contents, "man1-contents") + self.assertEqual(file_item.info()['x_object_manifest'], + "%s/%s/seg_lower" % + (self.env.container.name, self.env.segment_prefix)) def test_get_range(self): file_item = self.env.container.file('man1') @@ -1839,6 +1842,8 @@ class TestDlo(Base): self.assertEqual( file_contents, "aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeffffffffff") + # The copied object must not have X-Object-Manifest + self.assertTrue("x_object_manifest" not in file_item.info()) def test_copy_account(self): # dlo use same account and same container only @@ -1863,9 +1868,12 @@ class TestDlo(Base): self.assertEqual( file_contents, "aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeffffffffff") + # The copied object must not have X-Object-Manifest + self.assertTrue("x_object_manifest" not in file_item.info()) def test_copy_manifest(self): - # Copying the manifest should result in another manifest + # Copying the manifest with multipart-manifest=get query string + # should result in another manifest try: man1_item = self.env.container.file('man1') man1_item.copy(self.env.container.name, "copied-man1", @@ -1879,6 +1887,8 @@ class TestDlo(Base): self.assertEqual( copied_contents, "aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeee") + self.assertEqual(man1_item.info()['x_object_manifest'], + copied.info()['x_object_manifest']) finally: # try not to leave this around for other tests to stumble over self.env.container.file("copied-man1").delete()