Skip to content
This repository has been archived by the owner on Jan 3, 2024. It is now read-only.

Commit

Permalink
rgw/sfs: Fix multipart etag in response
Browse files Browse the repository at this point in the history
This fixes the problem found when downloading a multipart object with
s3cmd because the `etag` was not populated in the response.

It also happened with `aws cli` tool, but in that case the tool was not
showing any error, just that the `ETag` field was not set in the
response.

When creating the final object, the `etag` should be also set in the
object's attributes, because when filling the response for `RGWGetObj`
it iterates through the attributes to fill the response.

non-multipart uploads had the `etag` already in the attributes, that's
why it was working fine in that case.

Fixes: https://github.com/aquarist-labs/s3gw/issues/658
Signed-off-by: Xavi Garcia <[email protected]>
  • Loading branch information
0xavi0 committed Sep 20, 2023
1 parent 41f46c0 commit 007d05c
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 0 deletions.
34 changes: 34 additions & 0 deletions qa/rgw/store/sfs/tests/test-sfs-multipart.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import tempfile
from pydantic import BaseModel
import hashlib
import datetime

ACCESS_KEY = "test"
SECRET_KEY = "test"
Expand Down Expand Up @@ -447,3 +448,36 @@ def test_abort_multipart_upload(self):
except self.s3.meta.client.exceptions.NoSuchBucket:
has_error = True
self.assertTrue(has_error)

def test_multipart_upload_download_response(self):
bucket_name = self.create_bucket()
objname = self.get_random_object_name()
objsize = 100 * 1024**2 # 100 MB
objpath, md5 = self.gen_random_file(objname, objsize)

cfg = boto3.s3.transfer.TransferConfig(
multipart_threshold=10 * 1024, # 10 MB
max_concurrency=10,
multipart_chunksize=10 * 1024**2, # 10 MB
use_threads=True,
)

obj = self.s3.Object(bucket_name, objname)
obj.upload_file(objpath.as_posix(), Config=cfg)

response = self.s3c.get_object(Bucket=bucket_name, Key=objname)
self.assertTrue("ETag" in response)
# we uploaded 10 parts
self.assertTrue(response["ETag"].endswith('e-10"'))
self.assertTrue("LastModified" in response)
now = datetime.datetime.now()
# greater just in case we're running this test right at new year's eve
self.assertGreaterEqual(now.year, response["LastModified"].year)
self.assertTrue("ContentType" in response)
self.assertEqual("binary/octet-stream", response["ContentType"])
self.assertTrue("VersionId" in response)
self.assertNotEqual("", response["VersionId"])
self.assertTrue("ContentLength" in response)
self.assertEqual(objsize, response["ContentLength"])
self.delete_bucket(bucket_name)

3 changes: 3 additions & 0 deletions src/rgw/driver/sfs/multipart.cc
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,9 @@ int SFSMultipartUploadV2::complete(
}

objref->update_attrs(mp->attrs);
bufferlist etag_bl;
etag_bl.append(etag.c_str(), etag.size());
objref->set_attr(RGW_ATTR_ETAG, etag_bl);
objref->update_meta(
{.size = accounted_bytes,
.etag = etag,
Expand Down

0 comments on commit 007d05c

Please sign in to comment.