-
Notifications
You must be signed in to change notification settings - Fork 388
box/lua: disallow changing box options directly #8208
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
box/lua: disallow changing box options directly #8208
Conversation
Hi, here is something wrong with the validation of the options: tarantool> box.cfg{}
-- skipped --
tarantool> box.cfg.log_format = 'xxx'
---
- error: 'Incorrect value for option ''log_format'': expected ''plain'' or ''json'''
...
tarantool> box.cfg.log_format = 'xxx'
---
...
tarantool> box.cfg.log_format = 'yyy'
---
...
tarantool> box.cfg.log_format
---
- yyy
...
tarantool> box.cfg.log_level = 6
---
- error: 'Incorrect value for option ''log_format'': expected ''plain'' or ''json'''
...
-- ??? why log_format? Here is another issue, tarantool> box.cfg{}
-- skipped --
tarantool> box.cfg.log_level
---
- 5
...
tarantool> box.cfg.log_level = 6
2023-01-25 10:53:43.764 [339315] main/103/interactive/box.load_cfg I> set 'log_level' configuration option to 6
---
...
tarantool> box.cfg.log_level
---
- 6
...
tarantool> box.cfg
---
- replication_sync_timeout: 0
-- skipped --
log_level: 5
-- skipped -- |
9123d8c
to
7f48134
Compare
@Gumix, nice catch, thanks for noticing! By the way, I've added you to reviewers list, since we touch box-related interface. I believe you're the best candidate, 'cause you're the only one from Core team, who has already touched the patch :) |
45dd19e
to
1899e05
Compare
122dddf
to
d3e17bc
Compare
d3e17bc
to
8c1050d
Compare
cfd0dfd
to
7be80d6
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK for me, but I want to invite @nshy
test/box-luatest/gh_2867_raise_on_raw_box_cfg_modifications_test.lua
Outdated
Show resolved
Hide resolved
7be80d6
to
7827f2d
Compare
Well, there are two points regarding the current state of the PR:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ligurio, thanks for the patch! Please consider my comments below.
Furthermore, there are two confusing NOWRAP
entries in the commit message: if these are just typos, please fix it either.
I also guess that adding a docbot request will be nice, since such behaviour change should be added to the docs.
src/lua/init.lua
Outdated
local function patched_pairs(object) | ||
local mt = getmetatable(object) | ||
local iterable = (mt and mt.__name == 'box_cfg') and | ||
debug.getmetatable(object).__index or |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, this part is not quite right.
- It's unsafe, since
getmetatable
yields the value of__metatable
field if one is set (that can be e.g. string1). You need either to check the type ofmt
variable or to usedebug.getmetatable
to obtainobject
's metatable. I'd rather use the latter approach.
- There is no need in fetching
object
's metatable one more time in the line above (it's already stored inmt
, isn't it?) so just usemt.__index
for this.
Footnotes
-
Fortunately, all Lua strings share the metatable with overloaded index, so
mt.__name
in case of strings just yieldsnil
, but anyway in general case this is not safe. ↩
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Applied a patch where both issues fixed:
--- a/src/lua/init.lua
+++ b/src/lua/init.lua
@@ -35,9 +35,9 @@ local ROCKS_LUA_TEMPLATES = { ROCKS_LUA_PATH .. '/?.lua', ROCKS_LUA_PATH .. '/?/
-- box.cfg.
local builtin_pairs = rawget(_G, 'pairs')
local function patched_pairs(object)
- local mt = getmetatable(object)
+ local mt = debug.getmetatable(object)
local iterable = (mt and mt.__name == 'box_cfg') and
- debug.getmetatable(object).__index or
+ mt.__index or
object
return builtin_pairs(iterable)
end
I glanced over the patch out of curiosity and would give a few suggestions about the code layout.
-- pairs.lua
local M = {}
M.raw_pairs = pairs
-- table -> iterator generator
M.tracked = setmetatable({}, {__mode = 'k'})
_G.pairs = function(t)
local iter_gen = M.tracked[t] or M.raw_pairs
return iter_gen(t)
end
function M.add(t, iter_gen)
M.tracked[t] = iter_gen
end
function M.remove(t)
M.tracked[t] = nil
end
return M Usage: local internal_pairs = require('internal.pairs')
local real_table = {}
local proxy_table = {_iterate_over = real_table}
internal_pairs.add(proxy_table, function(self)
return pairs(self._iterate_over)
end)
-- or
local internal_pairs = require('internal.pairs')
local real_table = {}
local proxy_table = {}
internal_pairs.add(proxy_table, function(_self)
return pairs(real_table)
end) Feel free to ignore, I just pass here over. |
Added. |
7827f2d
to
6ddca75
Compare
As I got it right, these changes will make patched pairs available from outside:
On RFC discussion we decided to implement public version of patched pairs in scope of a separate ticket. Your snippet could be a good draft patch for this ticket. |
91ec30e
to
26c2ace
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ligurio, thanks for all changes made! LGTM now (but I'll wait for your answer for my question below prior to merging the PR).
src/box/lua/load_cfg.lua
Outdated
__newindex = function(self, key, value) -- luacheck: no unused args | ||
if template_cfg[key] ~= nil then | ||
local v = '<...>' | ||
if type(value) ~= 'table' then |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor: The only thing that bothers me here is the condition.
Is it possible to pass other "compex" types (such as cdata/udata/function) as a box.cfg
parameter value? If not, then everything is fine; if there are doubts we can just leave it as is and fix if/when one is found: yeah, it's bad when there is garbage in the error message, but not critical.
Feel free to ignore.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is a valid comment, thanks.
AFAIK, there are no values with non-printable types in box.cfg
. I wrote a small script that recursively looking for values in box.cfg table:
local function handle_obj(obj)
if type(obj) == "table" then
for k, v in pairs(obj) do
if not k then
path = path .. "." .. tostring(k)
end
handle_obj(k, path)
handle_obj(v, path)
end
end
if type(obj) == "number" or
type(obj) == "string" or
type(obj) == "boolean" then
return
end
local str = tostring(obj)
if string.match(str, "^function") and
string.match(str, "^table") and
string.match(str, "^cdata") and
string.match(str, "^userdata") then
local keyword = path .. "." .. str
print("\"" .. keyword .. "\"")
end
end
for obj in pairs(box.cfg) do
handle_obj(obj)
end
print("The end.")
os.exit(0)
and output:
[0] ~/sources/MRG/tarantool$ ./build/src/tarantool -e "box.cfg{};" box_cfg_types.lua
2023-02-10 15:30:12.096 [146593] main/103/box_cfg_types.lua I> Tarantool 2.11.0-entrypoint-984-gf3880e244
<snipped>
Start.
The end.
[0] ~/sources/MRG/tarantool$
Tarantool has a special table 'box.cfg' that includes configuration parameters. User could view that table, but it's direct modification had no effect - after assigning a new value it is actually "updated", but actual value remains the same. Such behaviour is a counterintuitive for our users and provides a bad experience. Proposed patch change this behaviour: new value assigned to parameter via direct access to table box.cfg raise an error. Before the patch: tarantool> box.cfg{} <snipped> tarantool> box.cfg.read_only=true --- ... tarantool> After the patch: NO_WRAP tarantool> box.cfg{} <snipped> tarantool> box.cfg.read_only=true --- - error: 'builtin/box/load_cfg.lua:973: Use box.cfg{read_only = true} for update' ... tarantool> NO_WRAP Closes tarantool#2867 @TarantoolBot document Title: Document changed behaviour on setting options to box.cfg directly Tarantool has a special table 'box.cfg' that includes configuration parameters. User could view that table, but it's direct modification has no effect - after assigning a new value it is actually "updated" but actual value remains the same. Such behaviour is a counterintuitive for our users and provides a bad experience. Now new value assigned to parameter via direct access to table box.cfg raise an error.
With previous commit rawset is not required anymore because internally we start to use raw_cfg. Follows up tarantool#2867 NO_CHANGELOG=code health NO_DOC=code health NO_TEST=code health
26c2ace
to
6f8993f
Compare
Tarantool has a special table 'box.cfg' that includes configuration
parameters. User could view that table, but it's direct modification has
no effect - after assigning a new value it is actually "updated" but
actual value remains the same. Such behaviour is a counterintuitive for
our users and provides a bad experience.
Proposed patch change this behaviour: new value assigned to parameter
via direct access to table box.cfg raise an error.
Before the patch:
After the patch:
NO_DOC=bugfix
Closes #2867