From 2c4b718162198e6db679113ce3196b391d8571e5 Mon Sep 17 00:00:00 2001 From: Seena Fallah Date: Mon, 3 Mar 2025 17:16:55 +0100 Subject: [PATCH] rgw: skip versioned entries to non-versioned buckets and vice-versa As instance field is preserved while replicating, this can make confusion for versioned buckets having non-versioned objects and non-versioned buckets having versioned objects. Fixes: https://tracker.ceph.com/issues/70486 Signed-off-by: Seena Fallah --- src/rgw/driver/rados/rgw_data_sync.cc | 8 +++ src/test/rgw/rgw_multi/tests.py | 96 +++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) diff --git a/src/rgw/driver/rados/rgw_data_sync.cc b/src/rgw/driver/rados/rgw_data_sync.cc index 7eb8db380889c..10fe3b227b38b 100644 --- a/src/rgw/driver/rados/rgw_data_sync.cc +++ b/src/rgw/driver/rados/rgw_data_sync.cc @@ -4520,6 +4520,14 @@ public: tn->log(0, "entry with empty obj name, skipping"); goto done; } + + // make sure versioned object only lands on versioned bucket and non-versioned object only lands on non-versioned bucket + if (key.instance.empty() == sync_pipe.dest_bucket_info.versioned()) { + set_status("skipping entry due to versioning mismatch"); + tn->log(0, SSTR("skipping entry due to versioning mismatch: " << key)); + goto done; + } + if (error_injection && rand() % 10000 < cct->_conf->rgw_sync_data_inject_err_probability * 10000.0) { tn->log(0, SSTR(": injecting data sync error on key=" << key.name)); diff --git a/src/test/rgw/rgw_multi/tests.py b/src/test/rgw/rgw_multi/tests.py index c01937932b72c..a08f36ca4c6c1 100644 --- a/src/test/rgw/rgw_multi/tests.py +++ b/src/test/rgw/rgw_multi/tests.py @@ -3946,3 +3946,99 @@ def test_bucket_replicaion_reject_objectlock_identical(): }] }) assert e.response['ResponseMetadata']['HTTPStatusCode'] == 400 + +@allow_bucket_replication +def test_bucket_replication_non_versioned_to_versioned(): + zonegroup = realm.master_zonegroup() + zonegroup_conns = ZonegroupConns(zonegroup) + + source = zonegroup_conns.non_account_rw_zones[0] + dest = zonegroup_conns.non_account_rw_zones[1] + + source_bucket = source.create_bucket(gen_bucket_name()) + dest_bucket = dest.create_bucket(gen_bucket_name()) + zonegroup_meta_checkpoint(zonegroup) + + # create replication configuration + response = source.s3_client.put_bucket_replication( + Bucket=source_bucket.name, + ReplicationConfiguration={ + 'Role': '', + 'Rules': [ + { + 'ID': 'rule1', + 'Status': 'Enabled', + 'Destination': { + 'Bucket': f'arn:aws:s3:::{dest_bucket.name}', + } + } + ] + } + ) + assert response['ResponseMetadata']['HTTPStatusCode'] == 200 + zonegroup_meta_checkpoint(zonegroup) + + # enable versioning on destination bucket + dest.s3_client.put_bucket_versioning( + Bucket=dest_bucket.name, + VersioningConfiguration={'Status': 'Enabled'} + ) + zonegroup_meta_checkpoint(zonegroup) + + # upload an object and wait for sync. + objname = 'dummy' + k = new_key(source, source_bucket, objname) + k.set_contents_from_string('foo') + zone_data_checkpoint(dest.zone, source.zone) + + # check that object not exists in destination bucket + e = assert_raises(ClientError, dest.s3_client.get_object, Bucket=dest_bucket.name, Key=objname) + assert e.response['Error']['Code'] == 'NoSuchKey' + +@allow_bucket_replication +def test_bucket_replication_versioned_to_non_versioned(): + zonegroup = realm.master_zonegroup() + zonegroup_conns = ZonegroupConns(zonegroup) + + source = zonegroup_conns.non_account_rw_zones[0] + dest = zonegroup_conns.non_account_rw_zones[1] + + source_bucket = source.create_bucket(gen_bucket_name()) + dest_bucket = dest.create_bucket(gen_bucket_name()) + zonegroup_meta_checkpoint(zonegroup) + + # create replication configuration + response = source.s3_client.put_bucket_replication( + Bucket=source_bucket.name, + ReplicationConfiguration={ + 'Role': '', + 'Rules': [ + { + 'ID': 'rule1', + 'Status': 'Enabled', + 'Destination': { + 'Bucket': f'arn:aws:s3:::{dest_bucket.name}', + } + } + ] + } + ) + assert response['ResponseMetadata']['HTTPStatusCode'] == 200 + zonegroup_meta_checkpoint(zonegroup) + + # enable versioning on source bucket + source.s3_client.put_bucket_versioning( + Bucket=source_bucket.name, + VersioningConfiguration={'Status': 'Enabled'} + ) + zonegroup_meta_checkpoint(zonegroup) + + # upload an object and wait for sync. + objname = 'dummy' + k = new_key(source, source_bucket, objname) + k.set_contents_from_string('foo') + zone_data_checkpoint(dest.zone, source.zone) + + # check that object not exists in destination bucket + e = assert_raises(ClientError, dest.s3_client.get_object, Bucket=dest_bucket.name, Key=objname) + assert e.response['Error']['Code'] == 'NoSuchKey' -- 2.39.5