Skip to content

Commit 8a5e11a

Browse files
CephRGWTest: Add Ceph RGW Cloud Sync integration test
Add integration test for the Cloud Sync feature proposed to the upstream `ceph-radosgw` Juju charm. Signed-off-by: Ionut Balutoiu <ibalutoiu@cloudbasesolutions.com>
1 parent b325283 commit 8a5e11a

File tree

1 file changed

+153
-0
lines changed

1 file changed

+153
-0
lines changed

zaza/openstack/charm_tests/ceph/tests.py

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,12 @@ class CephRGWTest(test_utils.BaseCharmTest):
657657
primary_rgw_unit = 'ceph-radosgw/0'
658658
secondary_rgw_app = 'secondary-ceph-radosgw'
659659
secondary_rgw_unit = 'secondary-ceph-radosgw/0'
660+
cloud_sync_rgw_app = 'cloud-sync-ceph-radosgw'
661+
# These S3 Juju apps are used for the cloud sync tests. They are deployed
662+
# using the following Juju charm: https://charmhub.io/minio-test
663+
# Their purpose is to provide S3 destinations for the cloud sync tests.
664+
cloud_sync_default_s3_app = 's3-default'
665+
cloud_sync_dev_s3_app = 's3-dev'
660666

661667
@classmethod
662668
def setUpClass(cls):
@@ -685,6 +691,15 @@ def multisite(self):
685691
except KeyError:
686692
return False
687693

694+
@property
695+
def cloud_sync(self):
696+
"""Determine whether Ceph cloud sync is used."""
697+
try:
698+
zaza_model.get_application(self.cloud_sync_rgw_app)
699+
return True
700+
except KeyError:
701+
return False
702+
688703
def get_rgwadmin_cmd_skeleton(self, unit_name):
689704
"""
690705
Get radosgw-admin cmd skeleton with rgw.hostname populated key.
@@ -856,6 +871,35 @@ def get_rgw_endpoint(self, unit_name: str):
856871
except KeyError:
857872
return "http://{}:80".format(unit_address)
858873

874+
def get_minio_boto3_client(self, app_name: str):
875+
"""Get boto3 client for MinIO application.
876+
877+
:param app_name: MinIO Juju app name.
878+
:type app_name: str
879+
"""
880+
leader_unit = zaza_model.get_lead_unit(app_name)
881+
unit_address = zaza_model.get_unit_public_address(
882+
leader_unit,
883+
self.model_name
884+
)
885+
886+
logging.debug("Minio Leader Unit: {}, Endpoint: {}".format(
887+
leader_unit.entity_id, unit_address))
888+
if unit_address is None:
889+
return None
890+
891+
app_config = zaza_model.get_application_config(app_name)
892+
port = app_config['port'].get('value')
893+
access_key = app_config['root-user'].get('value')
894+
access_secret = app_config['root-password'].get('value')
895+
896+
return boto3.resource(
897+
"s3",
898+
verify=False,
899+
endpoint_url="http://{}:{}".format(unit_address, port),
900+
aws_access_key_id=access_key,
901+
aws_secret_access_key=access_secret)
902+
859903
def configure_rgw_apps_for_multisite(self):
860904
"""Configure Multisite values on primary and secondary apps."""
861905
realm = 'zaza_realm'
@@ -877,6 +921,15 @@ def configure_rgw_apps_for_multisite(self):
877921
'zone': 'zaza_secondary'
878922
}
879923
)
924+
if self.cloud_sync:
925+
zaza_model.set_application_config(
926+
self.cloud_sync_rgw_app,
927+
{
928+
'realm': realm,
929+
'zonegroup': zonegroup,
930+
'zone': 'zaza_cloud_sync'
931+
}
932+
)
880933

881934
def clean_rgw_multisite_config(self, app_name):
882935
"""Clear Multisite Juju config values to default.
@@ -1048,6 +1101,13 @@ def test_003_object_storage_and_secondary_block(self):
10481101
"Non-Pristine RGW site can't be used as secondary"
10491102
}
10501103
}
1104+
if self.cloud_sync:
1105+
assert_state[self.cloud_sync_rgw_app] = {
1106+
"workload-status": "blocked",
1107+
"workload-status-message-prefix":
1108+
"multi-site configuration but primary/secondary "
1109+
"relation missing",
1110+
}
10511111
zaza_model.wait_for_application_states(states=assert_state,
10521112
timeout=900)
10531113

@@ -1066,6 +1126,99 @@ def test_003_object_storage_and_secondary_block(self):
10661126
zaza_model.block_until_unit_wl_status(self.secondary_rgw_unit,
10671127
'active')
10681128

1129+
def test_004_object_storage_cloud_sync(self):
1130+
"""Verify Ceph RGW Cloud Sync functionality."""
1131+
# Skip cloud sync tests if not compatible with bundle.
1132+
if not self.cloud_sync:
1133+
raise unittest.SkipTest('Skipping Cloud Sync Test')
1134+
1135+
obj_name = 'testfile'
1136+
# Syncs to default S3 target.
1137+
default_container_name = 'zaza-cloud-sync-container'
1138+
default_obj_data = 'Test data from Zaza'
1139+
# Syncs to dev S3 target.
1140+
dev_container_name = 'dev-zaza-cloud-sync-container'
1141+
dev_obj_data = 'Test dev data from Zaza'
1142+
1143+
# Configure cloud-sync multi-site relation.
1144+
logging.info('Configuring Cloud Sync Multisite')
1145+
self.configure_rgw_apps_for_multisite()
1146+
zaza_model.add_relation(
1147+
self.primary_rgw_app,
1148+
self.primary_rgw_app + ":primary",
1149+
self.cloud_sync_rgw_app + ":cloud-sync"
1150+
)
1151+
assert_state = {
1152+
self.secondary_rgw_app: {
1153+
"workload-status": "blocked",
1154+
"workload-status-message-prefix":
1155+
"multi-site configuration but primary/secondary "
1156+
"relation missing",
1157+
}
1158+
}
1159+
zaza_model.wait_for_application_states(states=assert_state,
1160+
timeout=900)
1161+
1162+
logging.info('Verifying Ceph RGW Cloud Sync functionality')
1163+
1164+
# Fetch Primary Endpoint Details.
1165+
primary_endpoint = self.get_rgw_endpoint(self.primary_rgw_unit)
1166+
self.assertNotEqual(primary_endpoint, None)
1167+
1168+
# Create RGW client and perform IO to be synced to both S3 targets.
1169+
access_key, secret_key = self.get_client_keys()
1170+
primary_client = boto3.resource("s3",
1171+
verify=False,
1172+
endpoint_url=primary_endpoint,
1173+
aws_access_key_id=access_key,
1174+
aws_secret_access_key=secret_key)
1175+
default_container = primary_client.Bucket(default_container_name)
1176+
default_container.create()
1177+
default_obj = primary_client.Object(default_container_name, obj_name)
1178+
default_obj.put(Body=default_obj_data)
1179+
dev_container = primary_client.Bucket(dev_container_name)
1180+
dev_container.create()
1181+
dev_obj = primary_client.Object(dev_container_name, obj_name)
1182+
dev_obj.put(Body=dev_obj_data)
1183+
1184+
# Wait for sync to complete.
1185+
logging.info('Waiting for Cloud Sync Data and Metadata to Synchronize')
1186+
self.wait_for_status(self.cloud_sync_rgw_app, is_primary=False)
1187+
1188+
# Create clients for the cloud-sync S3 targets.
1189+
default_s3_client = self.get_minio_boto3_client(
1190+
self.cloud_sync_default_s3_app
1191+
)
1192+
self.assertNotEqual(default_s3_client, None)
1193+
dev_s3_client = self.get_minio_boto3_client(self.cloud_sync_dev_s3_app)
1194+
self.assertNotEqual(dev_s3_client, None)
1195+
1196+
# Verify that data was properly synced.
1197+
logging.info('Verifying Synced Data on S3 Targets')
1198+
test_data = self.fetch_rgw_object(default_s3_client,
1199+
default_container_name,
1200+
obj_name)
1201+
self.assertEqual(test_data, default_obj_data)
1202+
test_data = self.fetch_rgw_object(dev_s3_client,
1203+
dev_container_name,
1204+
obj_name)
1205+
self.assertEqual(test_data, dev_obj_data)
1206+
1207+
# Perform cleanup.
1208+
logging.info('Performing Cleanup')
1209+
self.purge_bucket(self.primary_rgw_app, default_container_name)
1210+
self.purge_bucket(self.primary_rgw_app, dev_container_name)
1211+
1212+
# Wait for sync to complete.
1213+
self.wait_for_status(self.cloud_sync_rgw_app, is_primary=False)
1214+
1215+
# Validate that synced data was removed from the S3 targets.
1216+
logging.info('Verifying that data was deleted on the S3 targets')
1217+
with self.assertRaises(botocore.exceptions.ClientError):
1218+
default_s3_client.Object(default_container_name, obj_name).get()
1219+
with self.assertRaises(botocore.exceptions.ClientError):
1220+
dev_s3_client.Object(dev_container_name, obj_name).get()
1221+
10691222
def test_100_migration_and_multisite_failover(self):
10701223
"""Perform multisite migration and verify failover."""
10711224
container_name = 'zaza-container'

0 commit comments

Comments
 (0)