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"(