]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw: Add Bucket tags attribute for Lua scripts 67219/head
authorEmin Sunacoglu <emin.sunacoglu@clyso.com>
Thu, 11 Dec 2025 08:27:11 +0000 (09:27 +0100)
committermertsunacoglu <emin.sunacoglu@clyso.com>
Mon, 30 Mar 2026 07:51:05 +0000 (09:51 +0200)
Expose bucket tags (from RGW_ATTR_TAGS) to the RGW Lua Request.Bucket.Tags table, allowing lua scripts to read these tags.

Fixes: https://tracker.ceph.com/issues/74756
Signed-off-by: Emin Sunacoglu <emin.sunacoglu@clyso.com>
Apply suggestion from @yuvalif

Co-authored-by: Yuval Lifshitz <yuvalif@yahoo.com>
Signed-off-by: Emin Mert Sunacoglu <33792101+mertsunacoglu@users.noreply.github.com>
doc/radosgw/lua-scripting.rst
src/rgw/rgw_lua_request.cc
src/test/rgw/test_rgw_lua.cc

index 62bf004e4b7aea55205fb709b960b1a293322a24..5d7d6d1ed6741caf7df8ac0715fef73bfd716175 100644 (file)
@@ -198,6 +198,8 @@ Request Fields
 +----------------------------------------------------+----------+--------------------------------------------------------------+----------+-----------+----------+
 | ``Request.Bucket.Quota.Rounded``                   | boolean  | bucket quota is rounded to 4K                                | no       | no        | no       |
 +----------------------------------------------------+----------+--------------------------------------------------------------+----------+-----------+----------+
+| ``Request.Bucket.Tags``                            | table    | bucket tags                                                  | no      | no        | yes      |
++----------------------------------------------------+----------+--------------------------------------------------------------+----------+-----------+----------+
 | ``Request.Bucket.PlacementRule``                   | table    | bucket placement rule                                        | no       | no        | yes      |
 +----------------------------------------------------+----------+--------------------------------------------------------------+----------+-----------+----------+
 | ``Request.Bucket.PlacementRule.Name``              | string   | bucket placement rule name                                   | no       | no        | no       |
index 6f65a4c7ff04b30b2ce253450503ca464731b8e6..8c02b35fbc406f547683889ea23d4253631e0cba 100644 (file)
@@ -272,6 +272,46 @@ struct OwnerMetaTable : public EmptyMetaTable {
   }
 };
 
+struct BucketTagsTable : public EmptyMetaTable {
+  static int IndexClosure(lua_State* L) {
+    const auto bl = reinterpret_cast<bufferlist*>(lua_touserdata(L, lua_upvalueindex(SECOND_UPVAL)));
+    const char* key = luaL_checkstring(L, 2);
+    try {
+      RGWObjTags tags;
+      auto bl_it = bl->cbegin();
+      tags.decode(bl_it);
+
+      const auto& tag_map = tags.get_tags();
+      auto tag_it = tag_map.find(key);
+
+      if (tag_it != tag_map.end()) {
+        pushstring(L, tag_it->second);
+        return ONE_RETURNVAL;
+      }
+    } catch (const buffer::error& err) {
+      lua_pushnil(L);
+      return ONE_RETURNVAL;
+    }
+    lua_pushnil(L);
+    return ONE_RETURNVAL;
+  }
+
+  static int LenClosure(lua_State* L) {
+    const auto bl = reinterpret_cast<bufferlist*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+
+    try {
+      RGWObjTags tags;
+      auto bl_it = bl->cbegin();
+      tags.decode(bl_it);
+      lua_pushinteger(L, tags.get_tags().size());
+      return ONE_RETURNVAL;
+    } catch (const buffer::error& err) {
+      lua_pushinteger(L, 0);
+      return ONE_RETURNVAL;
+    }
+  }
+};
+
 struct BucketMetaTable : public EmptyMetaTable {
   static int IndexClosure(lua_State* L) {
     const auto name = table_name_upvalue(L);
@@ -300,6 +340,13 @@ struct BucketMetaTable : public EmptyMetaTable {
       pushtime(L, bucket->get_creation_time());
     } else if (strcasecmp(index, "MTime") == 0) {
       pushtime(L, bucket->get_modification_time());
+    } else if (strcasecmp(index, "Tags") == 0) {
+      auto it = s->bucket_attrs.find(RGW_ATTR_TAGS);
+      if (it != s->bucket_attrs.end()) {
+        create_metatable<BucketTagsTable>(L, name, index, false, &(it->second));
+      } else {
+        lua_pushnil(L);
+    }
     } else if (strcasecmp(index, "Quota") == 0) {
       create_metatable<QuotaMetaTable>(L, name, index, false, &(bucket->get_info().quota));
     } else if (strcasecmp(index, "PlacementRule") == 0) {
index 7509b82d4f8ed73dd9423f3dbd2daedce509a9a9..76de31cddef08c4d2471eb1d28364d77e51eac9a 100644 (file)
@@ -7,6 +7,7 @@
 #include "rgw_lua_data_filter.h"
 #include "rgw_sal_config.h"
 #include "rgw_perf_counters.h"
+#include "rgw_tag.h"
 
 using namespace std;
 using namespace rgw;
@@ -1723,4 +1724,55 @@ TEST(TestRGWLua, NotValidLua)
   const auto rc = lua::request::execute(nullptr, nullptr, &s, nullptr, script, return_code);
   ASSERT_EQ(rc, -1);
   EXPECT_NE(return_code, -EPERM);
-}
\ No newline at end of file
+}
+
+TEST(TestRGWLua, BucketTags)
+{
+  const std::string script = R"(
+    assert(Request.Bucket.Tags["Project"] == "Ceph")
+  )";
+
+  DEFINE_REQ_STATE;
+
+  RGWBucketInfo info;
+  info.bucket.tenant = "mytenant";
+  info.bucket.name = "myname";
+
+  RGWObjTags tags;
+  tags.add_tag("Project", "Ceph");
+
+  bufferlist bl;
+  tags.encode(bl);
+
+  s.bucket_attrs[RGW_ATTR_TAGS] = bl;
+  s.bucket.reset(new sal::RadosBucket(nullptr, info));
+
+  const auto rc = lua::request::execute(nullptr, nullptr, &s, nullptr, script);
+  ASSERT_EQ(rc, 0);
+}
+
+TEST(TestRGWLua, BucketTagsCount)
+{
+  const std::string script = R"(
+    assert(#Request.Bucket.Tags == 3)
+  )";
+
+  DEFINE_REQ_STATE;
+
+  RGWBucketInfo info;
+  info.bucket.name = "tag-test-bucket";
+
+  RGWObjTags tags;
+  tags.add_tag("Project", "Ceph");
+  tags.add_tag("Owner", "Ceph Team");
+  tags.add_tag("Status", "Testing");
+
+  bufferlist bl;
+  tags.encode(bl);
+
+  s.bucket_attrs[RGW_ATTR_TAGS] = bl;
+  s.bucket.reset(new sal::RadosBucket(nullptr, info));
+
+  const auto rc = lua::request::execute(nullptr, nullptr, &s, nullptr, script);
+  ASSERT_EQ(rc, 0);
+}