From: Yuval Lifshitz Date: Sun, 26 Apr 2026 15:17:54 +0000 (+0000) Subject: rgw/notifications: relax topic names validation X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=297279dce3e7986e81a1fe4e00e6dd56a6237862;p=ceph.git rgw/notifications: relax topic names validation Fixes: https://tracker.ceph.com/issues/76262 Signed-off-by: Yuval Lifshitz --- diff --git a/doc/radosgw/notifications.rst b/doc/radosgw/notifications.rst index 2fa909134833..ae64c15a4064 100644 --- a/doc/radosgw/notifications.rst +++ b/doc/radosgw/notifications.rst @@ -195,6 +195,10 @@ generated. A successful response includes the topic's `ARN`_ (the "Amazon Resource Name", a unique identifier used to reference the topic). To update a topic, use the same command that you used to create it (but when updating, use the name of an existing topic and different endpoint values). +Topic names must contain only alphanumeric characters, hyphens, and underscores, +and must be between 1 and 256 characters long. To relax these requirements, use: + +.. confval:: rgw_relaxed_topic_names .. tip:: Any notification already associated with the topic must be re-created in order for the topic to update. diff --git a/src/common/options/rgw.yaml.in b/src/common/options/rgw.yaml.in index 57000c5154b5..0ef9fdd830b9 100644 --- a/src/common/options/rgw.yaml.in +++ b/src/common/options/rgw.yaml.in @@ -4386,6 +4386,17 @@ options: flags: - startup with_legacy: true +- name: rgw_relaxed_topic_names + type: bool + level: advanced + desc: RGW enable relaxed topic names + long_desc: RGW enable relaxed topic names to allow changing existing topics + that were created before validation rules were implemented. This also allows re-creating + topics that were deleted, but match names that are already used externally (e.g. in Kafka) + default: false + services: + - rgw + with_legacy: true - name: rgw_lua_max_memory_per_state type: uint level: advanced diff --git a/src/rgw/rgw_rest_pubsub.cc b/src/rgw/rgw_rest_pubsub.cc index c2bae7b2e570..83357fd6c62b 100644 --- a/src/rgw/rgw_rest_pubsub.cc +++ b/src/rgw/rgw_rest_pubsub.cc @@ -253,7 +253,10 @@ class RGWPSCreateTopicOp : public RGWOp { int get_params() { topic_name = s->info.args.get("Name"); - if (!validate_topic_name(topic_name, s->err.message)) { + if (topic_name.empty() || + (!s->get_cct()->_conf.get_val("rgw_relaxed_topic_names") && + !validate_topic_name(topic_name, s->err.message)) + ) { return -EINVAL; } diff --git a/src/test/rgw/bucket_notification/api.py b/src/test/rgw/bucket_notification/api.py index b725afed3668..d0b0a2fafc2d 100644 --- a/src/test/rgw/bucket_notification/api.py +++ b/src/test/rgw/bucket_notification/api.py @@ -326,7 +326,6 @@ class PSTopicS3: def __init__(self, conn, topic_name, region, endpoint_args=None, opaque_data=None, policy_text=None): self.conn = conn self.topic_name = topic_name.strip() - assert self.topic_name self.topic_arn = '' self.attributes = {} if endpoint_args is not None: diff --git a/src/test/rgw/bucket_notification/test_bn.py b/src/test/rgw/bucket_notification/test_bn.py index 1155ed281351..a8212c6e7b71 100644 --- a/src/test/rgw/bucket_notification/test_bn.py +++ b/src/test/rgw/bucket_notification/test_bn.py @@ -904,6 +904,46 @@ def test_topic(): list_topics(0, tenant) +@pytest.mark.manual_test +def test_topic_name(): + """ test topic name validation """ + conn = connection() + # make sure there are no leftover topics + delete_all_topics(conn, '', get_config_cluster()) + + zonegroup = get_config_zonegroup() + bucket_name = gen_bucket_name() + invalid_topic_name = bucket_name + '+' + TOPIC_SUFFIX + rgw_client = f'client.rgw.{get_config_port()}' + + # fail to create topic + endpoint_address = 'http://127.0.0.1:7001/' + endpoint_args = 'push-endpoint='+endpoint_address+'&persistent=true' + topic_conf = PSTopicS3(conn, invalid_topic_name, zonegroup, endpoint_args=endpoint_args) + pytest.raises(Exception, topic_conf.set_config) + + # relax topic name validation + set_rgw_config_option(rgw_client, 'rgw_relaxed_topic_names', 'true', get_config_cluster()) + # create topic + expected_arn = 'arn:aws:sns:' + zonegroup + '::' + invalid_topic_name + topic_arn = topic_conf.set_config() + assert topic_arn == expected_arn + + set_rgw_config_option(rgw_client, 'rgw_relaxed_topic_names', 'false', get_config_cluster()) + + # get topic (should be possible regardless of the relaxed topic name setting) + parsed_result = get_topic(invalid_topic_name) + assert parsed_result['arn'] == expected_arn + + # delete topic + status = topic_conf.del_config() + assert status == 200 + + # maks sure that empty topic names are invalid regardless of flag + empty_topic_conf = PSTopicS3(conn, "", zonegroup, endpoint_args=endpoint_args) + pytest.raises(Exception, empty_topic_conf.set_config) + + @pytest.mark.basic_test def test_topic_admin(): """ test topics set/get/delete """