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

Commit

Permalink
rgw/sfs: set default obj-lock retention mode for multipart objects
Browse files Browse the repository at this point in the history
When an object is uploaded with multipart, for object-lock enabled buckets,
the default rentention mode must be set in the object's attributes
(when a retention mode is not explicitely set by the user for the object).
Therefore, RGW_ATTR_OBJECT_RETENTION must be set in the attrs of each part
being uploaded in the SFSMultipartUploadV2::complete() function.

Fixes: https://github.com/aquarist-labs/s3gw/issues/761
Signed-off-by: Giuseppe Baccini <giuseppe.baccini@suse.com>
  • Loading branch information
Giuseppe Baccini committed Nov 23, 2023
1 parent b13b9dc commit 6bc54b5
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 0 deletions.
50 changes: 50 additions & 0 deletions qa/rgw/store/sfs/tests/test-sfs-object-locking.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class ObjectLockingTests(unittest.TestCase):
BUCKET_NAME_3 = "bobjlockenabled3"
BUCKET_NAME_4 = "bobjlockenabled4"
BUCKET_NAME_5 = "bobjlockenabled5"
BUCKET_NAME_6 = "bobjlockenabled6"

ObjVersions = {}

Expand Down Expand Up @@ -462,6 +463,55 @@ def test_object_locking_legal_hold(self):

self.assertTrue(response["ResponseMetadata"]["HTTPStatusCode"] == 204)

def test_multipart_upload_has_default_retention(self):
self.ensure_bucket(ObjectLockingTests.BUCKET_NAME_6, True)

self.s3_client.put_object_lock_configuration(
Bucket=ObjectLockingTests.BUCKET_NAME_6,
ObjectLockConfiguration={
"ObjectLockEnabled": "Enabled",
"Rule": {"DefaultRetention": {"Mode": "COMPLIANCE", "Years": 7}},
},
)

res = self.s3_client.create_multipart_upload(
Bucket=ObjectLockingTests.BUCKET_NAME_6, Key="key.1"
)

upload_id = res["UploadId"]
parts_lst = []
res = self.s3_client.upload_part(
Body="data",
Bucket=ObjectLockingTests.BUCKET_NAME_6,
Key="key.1",
UploadId=upload_id,
PartNumber=1,
)
parts_lst.append({"ETag": res["ETag"], "PartNumber": 1})
self.s3_client.complete_multipart_upload(
Bucket=ObjectLockingTests.BUCKET_NAME_6,
Key="key.1",
UploadId=upload_id,
MultipartUpload={"Parts": parts_lst},
)

response = self.s3_client.list_object_versions(
Bucket=ObjectLockingTests.BUCKET_NAME_6, Prefix="key.1"
)

for version in response["Versions"]:
if version["Key"] == "key.1" and version["IsLatest"] == True:
self.ObjVersions["key.1.6"] = version["VersionId"]
print(self.ObjVersions["key.1.6"])

response = self.s3_client.get_object_retention(
Bucket=ObjectLockingTests.BUCKET_NAME_6,
Key="key.1",
VersionId=self.ObjVersions["key.1.6"],
)

self.check_object_retention(response, "COMPLIANCE", 7, "Years")


if __name__ == "__main__":
if len(sys.argv) == 2:
Expand Down
16 changes: 16 additions & 0 deletions src/rgw/driver/sfs/multipart.cc
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,22 @@ int SFSMultipartUploadV2::complete(
return -ERR_INTERNAL_ERROR;
}

// for object-locking enabled buckets, set the bucket's object-locking
// profile when not defined on the MP part
if (bucketref->get_info().obj_lock_enabled() &&
bucketref->get_info().obj_lock.has_rule()) {
auto iter = mp->attrs.find(RGW_ATTR_OBJECT_RETENTION);
if (iter == mp->attrs.end()) {
ceph::real_time lock_until_date =
bucketref->get_info().obj_lock.get_lock_until_date(
ceph::real_clock::now()
);
std::string mode = bucketref->get_info().obj_lock.get_mode();
RGWObjectRetention obj_retention(mode, lock_until_date);
encode(obj_retention, mp->attrs[RGW_ATTR_OBJECT_RETENTION]);
}
}

// Server-side encryption: The decryptor needs a manifest to
// identify encrypted chunks. Each MP part corresponds to a chunk.
if (mp->attrs.find(RGW_ATTR_CRYPT_MODE) != mp->attrs.end()) {
Expand Down

0 comments on commit 6bc54b5

Please sign in to comment.