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>
+----------------------------------------------------+----------+--------------------------------------------------------------+----------+-----------+----------+
| ``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 |
}
};
+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);
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) {
#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;
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);
+}