]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw/notifications: relax topic names validation 68622/head
authorYuval Lifshitz <ylifshit@ibm.com>
Sun, 26 Apr 2026 15:17:54 +0000 (15:17 +0000)
committerYuval Lifshitz <ylifshit@ibm.com>
Thu, 30 Apr 2026 08:21:06 +0000 (08:21 +0000)
Fixes: https://tracker.ceph.com/issues/76262
Signed-off-by: Yuval Lifshitz <ylifshit@ibm.com>
doc/radosgw/notifications.rst
src/common/options/rgw.yaml.in
src/rgw/rgw_rest_pubsub.cc
src/test/rgw/bucket_notification/api.py
src/test/rgw/bucket_notification/test_bn.py

index 2fa9091348333d533ecc93f0ed73ca8b41899dcb..ae64c15a4064b019ad260b4285a9dadc7d296b0a 100644 (file)
@@ -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.
index 57000c5154b53618b4ddd61a8a75e90d3fa7e87c..0ef9fdd830b975a2fdb1bfeb870ed0d8addabb2c 100644 (file)
@@ -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
index c2bae7b2e57094c75fc945ccc23b9e90924913a2..83357fd6c62bc9d7de4046a8c05a908014ee2ae4 100644 (file)
@@ -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<bool>("rgw_relaxed_topic_names") &&
+        !validate_topic_name(topic_name, s->err.message))
+       ) {
       return -EINVAL;
     }
 
index b725afed36688da213b5b6bee98d27d6df9c6b62..d0b0a2fafc2d372dfb972c9f7b5e7fdce4962c46 100644 (file)
@@ -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:
index 1155ed2813512ec76e2a391aaf5597cb142d6397..a8212c6e7b714dc89f2594987cbba800dbd7f221 100644 (file)
@@ -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 """