diff --git a/doc/radosgw/lua-scripting.rst b/doc/radosgw/lua-scripting.rst
index 6c79dce5c0572..a5dbc8710e165 100644
--- a/doc/radosgw/lua-scripting.rst
+++ b/doc/radosgw/lua-scripting.rst
@@ -202,7 +202,7 @@ Request Fields
+----------------------------------------------------+----------+--------------------------------------------------------------+----------+-----------+----------+
| ``Request.HTTP.Resources`` | table | string to string resource map | yes | no | no |
+----------------------------------------------------+----------+--------------------------------------------------------------+----------+-----------+----------+
-| ``Request.HTTP.Metadata`` | table | string to string metadata map | yes | no | no |
+| ``Request.HTTP.Metadata`` | table | string to string metadata map | yes | yes | no |
+----------------------------------------------------+----------+--------------------------------------------------------------+----------+-----------+----------+
| ``Request.HTTP.Host`` | string | host name | no | no | no |
+----------------------------------------------------+----------+--------------------------------------------------------------+----------+-----------+----------+
@@ -290,7 +290,6 @@ Lua Code Samples
- Use of operations log only in case of errors:
.. code-block:: lua
-
if Request.Response.HTTPStatusCode ~= 200 then
RGWDebugLog("request is bad, use ops log")
@@ -306,3 +305,22 @@ Lua Code Samples
Request.Response.Message = " something bad happened :-( "
end
+- Add metadata to objects that was not originally sent by the client:
+
+In the `preRequest` context we should add:
+
+.. code-block:: lua
+
+ if Request.RGWOp == 'put_obj' then
+ Request.HTTP.Metadata["x-amz-meta-mydata"] = "my value"
+ end
+
+In the `postRequest` context we look at the metadata:
+
+.. code-block:: lua
+
+ RGWDebugLog("number of metadata entries is: " .. #Request.HTTP.Metadata)
+ for k, v in pairs(Request.HTTP.Metadata) do
+ RGWDebugLog("key=" .. k .. ", " .. "value=" .. v)
+ end
+
diff --git a/src/rgw/rgw_lua_request.cc b/src/rgw/rgw_lua_request.cc
index 8a65426af33f7..6e3b148df3aac 100644
--- a/src/rgw/rgw_lua_request.cc
+++ b/src/rgw/rgw_lua_request.cc
@@ -80,7 +80,7 @@ struct ResponseMetaTable : public EmptyMetaTable {
} else {
throw_unknown_field(index, TableName());
}
- return ONE_RETURNVAL;
+ return NO_RETURNVAL;
}
};
@@ -244,7 +244,22 @@ struct ObjectMetaTable : public EmptyMetaTable {
}
};
-template>
+typedef int MetaTableClosure(lua_State* L);
+
+template
+int StringMapWriteableNewIndex(lua_State* L) {
+ const auto map = reinterpret_cast(lua_touserdata(L, lua_upvalueindex(1)));
+
+ ceph_assert(lua_isstring(L, -1));
+ ceph_assert(lua_isstring(L, -2));
+ const char* value = lua_tostring(L, -1);
+ const char* index = lua_tostring(L, -2);
+ map->insert_or_assign(index, value);
+ return NO_RETURNVAL;
+}
+
+template,
+ MetaTableClosure NewIndex=EmptyMetaTable::NewIndexClosure>
struct StringMapMetaTable : public EmptyMetaTable {
static std::string TableName() {return "StringMap";}
@@ -265,6 +280,10 @@ struct StringMapMetaTable : public EmptyMetaTable {
return ONE_RETURNVAL;
}
+ static int NewIndexClosure(lua_State* L) {
+ return NewIndex(L);
+ }
+
static int PairsClosure(lua_State* L) {
auto map = reinterpret_cast(lua_touserdata(L, lua_upvalueindex(1)));
ceph_assert(map);
@@ -628,7 +647,7 @@ struct HTTPMetaTable : public EmptyMetaTable {
create_metatable>(L, false,
const_cast*>(&(info->args.get_sub_resources())));
} else if (strcasecmp(index, "Metadata") == 0) {
- create_metatable>(L, false, &(info->x_meta_map));
+ create_metatable>>(L, false, &(info->x_meta_map));
} else if (strcasecmp(index, "Host") == 0) {
pushstring(L, info->host);
} else if (strcasecmp(index, "Method") == 0) {
diff --git a/src/rgw/rgw_lua_utils.h b/src/rgw/rgw_lua_utils.h
index 2c4d724a6f25a..e3a7a132cb18e 100644
--- a/src/rgw/rgw_lua_utils.h
+++ b/src/rgw/rgw_lua_utils.h
@@ -43,6 +43,7 @@ constexpr auto THREE_UPVALS = 3;
constexpr auto FOUR_UPVALS = 4;
constexpr auto FIVE_UPVALS = 5;
+constexpr auto NO_RETURNVAL = 0;
constexpr auto ONE_RETURNVAL = 1;
constexpr auto TWO_RETURNVALS = 2;
constexpr auto THREE_RETURNVALS = 3;
@@ -143,21 +144,21 @@ struct EmptyMetaTable {
// to change, overload this function in the derived
static int NewIndexClosure(lua_State* L) {
throw std::runtime_error("trying to write to readonly field");
- return 1;
+ return NO_RETURNVAL;
}
// by default nothing is iterable
// to change, overload this function in the derived
static int PairsClosure(lua_State* L) {
throw std::runtime_error("trying to iterate over non-iterable field");
- return 1;
+ return ONE_RETURNVAL;
}
// by default nothing is iterable
// to change, overload this function in the derived
static int LenClosure(lua_State* L) {
throw std::runtime_error("trying to get length of non-iterable field");
- return 1;
+ return ONE_RETURNVAL;
}
static void throw_unknown_field(const std::string& index, const std::string& table) {
diff --git a/src/test/rgw/test_rgw_lua.cc b/src/test/rgw/test_rgw_lua.cc
index 2df08bd3c2812..6cae8ebf0182f 100644
--- a/src/test/rgw/test_rgw_lua.cc
+++ b/src/test/rgw/test_rgw_lua.cc
@@ -323,6 +323,48 @@ TEST(TestRGWLua, Tags)
ASSERT_EQ(rc, 0);
}
+TEST(TestRGWLua, TagsNotWriteable)
+{
+ const std::string script = R"(
+ Request.Tags["hello"] = "goodbye"
+ )";
+
+ DEFINE_REQ_STATE;
+ s.tagset.add_tag("hello", "world");
+
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "put_obj", script);
+ ASSERT_NE(rc, 0);
+}
+
+TEST(TestRGWLua, Metadata)
+{
+ const std::string script = R"(
+ print("number of metadata entries is: " .. #Request.HTTP.Metadata)
+ for k, v in pairs(Request.HTTP.Metadata) do
+ print("key=" .. k .. ", " .. "value=" .. v)
+ end
+ print("value of 'hello' is:")
+ print(Request.HTTP.Metadata["hello"])
+ print("value of 'kaboom' is:")
+ print(Request.HTTP.Metadata["kaboom"])
+ Request.HTTP.Metadata["hello"] = "goodbye"
+ Request.HTTP.Metadata["kaboom"] = "boom"
+ print("new number of metadata entries is: " .. #Request.HTTP.Metadata)
+ print("new value of 'hello' is:")
+ print(Request.HTTP.Metadata["hello"])
+ print("new value of 'kaboom' is:")
+ print(Request.HTTP.Metadata["kaboom"])
+ )";
+
+ DEFINE_REQ_STATE;
+ s.info.x_meta_map["hello"] = "world";
+ s.info.x_meta_map["foo"] = "bar";
+ s.info.x_meta_map["ka"] = "boom";
+
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "put_obj", script);
+ ASSERT_EQ(rc, 0);
+}
+
TEST(TestRGWLua, Acl)
{
const std::string script = R"(