diff --git a/changelogs/unreleased/gh-8051-set-box-cfg-thru-env.md b/changelogs/unreleased/gh-8051-set-box-cfg-thru-env.md new file mode 100644 index 0000000000000000000000000000000000000000..58a3659a7a548d82d709714122e5f39523cdeb58 --- /dev/null +++ b/changelogs/unreleased/gh-8051-set-box-cfg-thru-env.md @@ -0,0 +1,4 @@ +## feature/box/cfg + +* Implemented a way to set a table as box.cfg{} options value via + environment variables (gh-8051). diff --git a/src/box/lua/load_cfg.lua b/src/box/lua/load_cfg.lua index 3ccf9bc65ed9b3314aa6a8737de5875ab00650bb..d4f3fae9e0dad4381e0b387a55995cafa9eb84ac 100644 --- a/src/box/lua/load_cfg.lua +++ b/src/box/lua/load_cfg.lua @@ -1151,7 +1151,30 @@ local function get_option_from_env(option) -- This code lean on the existing set of template_cfg -- types for simplicity. - if param_type:find('table') and raw_value:find(',') then + if param_type:find('table') and (raw_value:startswith('{') or + raw_value:startswith('[')) then + return json.decode(raw_value) + elseif param_type:find('table') and raw_value:find('=') then + assert(not param_type:find('boolean')) + local res = {} + for _, v in ipairs(raw_value:split(',')) do + local eq = v:find('=') + if eq == nil then + error(err_msg_fmt:format(env_var_name, option, + 'in `key=value` or `value` format')) + end + local lhs = string.sub(v, 1, eq - 1) + local rhs = string.sub(v, eq + 1) + + if lhs == '' then + error(err_msg_fmt:format(env_var_name, option, + 'in `key=value` or `value` format, ' .. + '`key` must not be empty')) + end + res[lhs] = tonumber(rhs) or rhs + end + return res + elseif param_type:find('table') and raw_value:find(',') then assert(not param_type:find('boolean')) local res = {} for i, v in ipairs(raw_value:split(',')) do diff --git a/test/box-luatest/gh_8051_set_box_cfg_thru_env_test.lua b/test/box-luatest/gh_8051_set_box_cfg_thru_env_test.lua new file mode 100644 index 0000000000000000000000000000000000000000..fcbec619ec846b9047b8b7b2344129a35a1cab53 --- /dev/null +++ b/test/box-luatest/gh_8051_set_box_cfg_thru_env_test.lua @@ -0,0 +1,92 @@ +local server = require('luatest.server') +local popen = require('popen') +local clock = require('clock') + +local t = require('luatest') +local g = t.group() + +g.after_each = function() + if g.server ~= nil then + g.server:stop() + end +end + +local TARANTOOL_PATH = arg[-1] + +local function popen_run(command) + local cmd = TARANTOOL_PATH .. ' -i 2>&1' + local ph = popen.new({cmd}, { + shell = true, + setsid = true, + group_signal = true, + stdout = popen.opts.PIPE, + stderr = popen.opts.DEVNULL, + stdin = popen.opts.PIPE, + }) + t.assert(ph, 'process is not up') + + ph:write(command) + + local output = '' + local time_quota = 10.0 + local start_time = clock.monotonic() + while clock.monotonic() - start_time < time_quota do + local chunk = ph:read({timeout = 1.0}) + if chunk == '' or chunk == nil then + -- EOF or error + break + end + output = output .. chunk + end + + ph:close() + return output +end + +g.test_json_table_curly_bracket = function() + local env = {["TT_METRICS"] = '{"labels":{"alias":"gh_8051"},' .. + '"include":"all","exclude":["vinyl"]}'} + + g.server = server:new{alias='json_table_curly_bracket', env=env} + g.server:start() + + t.assert_equals(g.server:get_box_cfg().metrics.labels.alias, 'gh_8051') +end + +g.test_json_table_square_bracket = function() + local res = popen_run([=[ + os.setenv('TT_LISTEN', '["localhost:0"]') + box.cfg{} + box.cfg.listen + ]=]) + + t.assert_str_contains(res, "- ['localhost:0']") +end + +g.test_plain_table = function() + local env = {["TT_LOG_MODULES"] = 'aaa=info,bbb=error'} + + g.server = server:new{alias='plain_table', env=env} + g.server:start() + + t.assert_equals(g.server:get_box_cfg().log_modules, + {['aaa'] = 'info', ['bbb'] = 'error'}) +end + +g.test_format_error = function() + local res = popen_run([[ + os.setenv('TT_LOG_MODULES', 'aaa=info,bbb') + box.cfg{} + ]]) + + t.assert_str_contains(res, "in `key=value` or `value` format'") +end + +g.test_format_error_empty_key = function() + local res = popen_run([[ + os.setenv('TT_LOG_MODULES', 'aaa=info,=error') + box.cfg{} + ]]) + + t.assert_str_contains(res, "`key` must not be empty'") +end