Tarantool development patches archive
 help / color / mirror / Atom feed
* [Tarantool-patches] [PATCH] box: set cfg via environment variables
@ 2021-03-14 15:34 Roman Khabibov via Tarantool-patches
  2021-03-19 12:38 ` Leonid Vasiliev via Tarantool-patches
  2021-03-19 13:23 ` Leonid Vasiliev via Tarantool-patches
  0 siblings, 2 replies; 7+ messages in thread
From: Roman Khabibov via Tarantool-patches @ 2021-03-14 15:34 UTC (permalink / raw)
  To: tarantool-patches

Add ability to set box.cfg options via env variables. These
variables should have name "TT_<OPTION>". When Tarantool instance
is started under tarantoolctl utility env variables options have
more priority than instance file's cfg.

Closes #5602

@TarantoolBot document
Title: Set box.cfg options via env variables

Now, it is possible to set box.cfg options via environment
variables. The name of variable should correspond the followng
pattern: "TT_<NAME>", where <NAME> is an option name. For example:
"TT_LISTEN", "TT_READAHEAD".
If it is complex value, for example, "replication" can be set by a
table, the corresponding environment varibale value has the syntax
of lua array (without keys):

`export TT_REPLICATION="{'uri1', 'uri2'}"`

Note that every value in arary must be quoted.
---

@ChangeLog
* Add ability to set box.cfg options via environment variables (gh-5602).

Branch: https://github.com/tarantool/tarantool/tree/romanhabibov/gh-5602-environment-variables
Issue: https://github.com/tarantool/tarantool/issues/5602

 src/box/lua/load_cfg.lua                      | 74 +++++++++++++++++++
 test/app-tap/tarantoolctl.test.lua            | 33 ++++++++-
 .../gh-5602-environment-vars-cfg.result       | 42 +++++++++++
 .../gh-5602-environment-vars-cfg.test.lua     | 53 +++++++++++++
 test/box-tap/gh-5602_script.lua               | 70 ++++++++++++++++++
 5 files changed, 271 insertions(+), 1 deletion(-)
 create mode 100644 test/box-tap/gh-5602-environment-vars-cfg.result
 create mode 100755 test/box-tap/gh-5602-environment-vars-cfg.test.lua
 create mode 100755 test/box-tap/gh-5602_script.lua

diff --git a/src/box/lua/load_cfg.lua b/src/box/lua/load_cfg.lua
index 574c8bef4..eace25d46 100644
--- a/src/box/lua/load_cfg.lua
+++ b/src/box/lua/load_cfg.lua
@@ -616,6 +616,79 @@ setmetatable(box, {
      end
 })
 
+local function process_option_value(option, raw_value)
+    local param_type = template_cfg[option]
+    if param_type == nil then
+        return nil
+    end
+    local value = nil
+    if param_type == 'number' or option == 'log_level' then
+        value = tonumber(raw_value)
+        if value == nil then
+            error("Environment variable "..'TT_'..string.upper(option).." has"..
+                  " incorrect value for option '"..option.."': should be "..
+                  "converted to number")
+        end
+    elseif param_type == 'boolean' or option == 'log_nonblock' then
+        if raw_value == 'true' then
+            value = true
+        elseif raw_value == 'false' then
+            value = false
+        else
+            error("Environment variable "..'TT_'..string.upper(option).." has"..
+                  " incorrect value for option '"..option.."': should be "..
+                  "'true' or 'false'")
+        end
+    elseif param_type == 'string, number' then
+        value = tonumber(raw_value)
+        if value == nil then
+            value = raw_value
+        end
+    elseif param_type == 'string, number, table' then
+        if string.sub(raw_value, 1, 1) == '{' and
+           string.sub(raw_value, -1) == '}' then
+            value = string.sub(raw_value, 2, -2)
+            value = require('csv').load(value)[1]
+            for i, str in pairs(value) do
+                if not (string.sub(str, 1, 1) == '\'' and
+                        string.sub(str, -1) == '\'') and
+                   not (string.sub(str, 1, 1) == '"' and
+                        string.sub(str, -1) == '"') then
+                    error("Environment variable "..'TT_'..string.upper(option)..
+                          " has incorrect value for option '"..option.."': "..
+                          "elements of array should be quoted strings")
+                end
+                str = string.sub(str, 2, -2)
+                value[i] = str
+            end
+        else
+            value = tonumber(raw_value)
+            if value == nil then
+                value = raw_value
+            end
+        end
+    else
+        value = raw_value
+    end
+
+    return value
+end
+
+local function collect_env_cfg(cfg)
+    if cfg == nil then
+        cfg = {}
+    end
+    for option, _ in pairs(template_cfg) do
+        if cfg[option] == nil or os.getenv('TARANTOOLCTL') == 'true' then
+            local env_var_name = 'TT_'..string.upper(option)
+            local raw_value = os.getenv(env_var_name)
+            if raw_value ~= nil then
+                cfg[option] = process_option_value(option, raw_value)
+            end
+        end
+    end
+end
+
 -- Whether box is loaded.
 --
 -- `false` when box is not configured or when the initialization
@@ -627,6 +700,7 @@ setmetatable(box, {
 local box_is_configured = false
 
 local function load_cfg(cfg)
+    collect_env_cfg(cfg)
     -- A user may save box.cfg (this function) before box loading
     -- and call it afterwards. We should reconfigure box in the
     -- case.
diff --git a/test/app-tap/tarantoolctl.test.lua b/test/app-tap/tarantoolctl.test.lua
index 3c3023d34..1f15fe103 100755
--- a/test/app-tap/tarantoolctl.test.lua
+++ b/test/app-tap/tarantoolctl.test.lua
@@ -160,7 +160,7 @@ local function merge(...)
 end
 
 local test = tap.test('tarantoolctl')
-test:plan(8)
+test:plan(9)
 
 -- basic start/stop test
 -- must be stopped afterwards
@@ -632,4 +632,35 @@ test:test('filter_xlog', function(test)
     end
 end)
 
+-- gh-5602: Check that environment cfg variables are more
+-- prioritized than tarantoolctl cfg.
+do
+    local dir = fio.tempdir()
+    local code = [[ box.cfg ]]
+    create_script(dir, 'show_cfg.lua',  code)
+    local code = [[ box.cfg{listen=3302, readahead=10001} ]]
+    create_script(dir, 'cfg_script.lua', code)
+
+    local status, err = pcall(function()
+        test:test("check answers in case of call", function(test_i)
+            test_i:plan(4)
+            os.setenv('TT_LISTEN', '3301')
+            os.setenv('TT_READAHEAD', '10000')
+            check_ok(test_i, dir, 'start', 'cfg_script', 0)
+            tctl_wait_start(dir, 'cfg_script')
+            check_ok(test_i, dir, 'eval',  'cfg_script show_cfg.lua', 0,
+                     '---\n- 3301\n- 10000\n...', nil)
+            check_ok(test_i, dir, 'stop', 'cfg_script', 0)
+        end)
+    end)
+
+    cleanup_instance(dir, 'cfg_script')
+    recursive_rmdir(dir)
+
+    if status == false then
+        print(("Error: %s"):format(err))
+        os.exit()
+    end
+end
+
 os.exit(test:check() == true and 0 or -1)
diff --git a/test/box-tap/gh-5602-environment-vars-cfg.result b/test/box-tap/gh-5602-environment-vars-cfg.result
new file mode 100644
index 000000000..495e02a73
--- /dev/null
+++ b/test/box-tap/gh-5602-environment-vars-cfg.result
@@ -0,0 +1,42 @@
+TAP version 13
+1..5
+ok - box.cfg is successful
+ok - listen
+ok - readahead
+ok - strip_core
+ok - log_format is not set
+TAP version 13
+1..7
+ok - box.cfg is successful
+ok - listen
+ok - replication is table
+ok - replication URI 1
+ok - replication URI 2
+ok - replication_connect_timeout
+ok - replication_synchro_quorum
+TAP version 13
+1..3
+ok - box.cfg is successful
+ok - box.cfg{} background value is prioritized
+ok - box.cfg{} vinyl_timeout value is prioritized
+TAP version 13
+1..2
+ok - box.cfg is not successful
+ok - bad sql_cache_size value
+TAP version 13
+1..2
+ok - box.cfg is not successful
+builtin/box/load_cfg.lua:19: builtin/box/load_cfg.lua:638: Environment variable TT_STRIP_CORE has incorrect value for option 'strip_core': should be 'true' or 'false'
+ok - bad strip_core value
+TAP version 13
+1..2
+ok - box.cfg is not successful
+builtin/box/load_cfg.lua:19: builtin/box/load_cfg.lua:657: Environment variable TT_REPLICATION has incorrect value for option 'replication': elements of array should be quoted strings
+ok - bad replication value
+TAP version 13
+ok - set 1
+ok - set 2
+ok - set 3
+ok - set 4
+ok - set 5
+ok - set 6
diff --git a/test/box-tap/gh-5602-environment-vars-cfg.test.lua b/test/box-tap/gh-5602-environment-vars-cfg.test.lua
new file mode 100755
index 000000000..84b49b7eb
--- /dev/null
+++ b/test/box-tap/gh-5602-environment-vars-cfg.test.lua
@@ -0,0 +1,53 @@
+#!/usr/bin/env tarantool
+
+local os = require('os')
+local fio = require('fio')
+local tap = require('tap')
+local test = tap.test('gh-5602')
+
+-- gh-5602: Check that environment cfg variables working.
+local TARANTOOL_PATH = arg[-1]
+local script_name = 'gh-5602_script.lua'
+local path_to_script = fio.pathjoin(
+        os.getenv('PWD'),
+        'box-tap',
+        script_name)
+local function shell_command(set, i)
+    string.gsub(set, 'TT_', '')
+    return ('%s %s %s %d'):format(
+        set,
+        TARANTOOL_PATH,
+        path_to_script,
+        i)
+end
+
+local set_1 = 'TT_LISTEN=3301 ' ..
+              'TT_READAHEAD=10000 ' ..
+              'TT_STRIP_CORE=false ' ..
+              'TT_LOG_FORMAT=json'
+local set_2 = 'TT_LISTEN=3301 ' ..
+              'TT_REPLICATION=\"{\'0.0.0.0:12345\', ' ..
+                              '\'1.1.1.1:12345\'}\" ' ..
+              'TT_REPLICATION_CONNECT_TIMEOUT=0.01 ' ..
+              'TT_REPLICATION_SYNCHRO_QUORUM=\'4 + 1\''
+local set_3 = 'TT_BACKGROUND=true ' ..
+              'TT_VINYL_TIMEOUT=60.1'
+local set_4 = 'TT_SQL_CACHE_SIZE=a'
+local set_5 = 'TT_STRIP_CORE=a'
+local set_6 = 'TT_REPLICATION=\"{0.0.0.0:12345\', ' ..
+                              '\'1.1.1.1:12345\'}\"'
+local sets = {}
+sets[1] = set_1
+sets[2] = set_2
+sets[3] = set_3
+sets[4] = set_4
+sets[5] = set_5
+sets[6] = set_6
+
+for i, set in ipairs(sets) do
+    local tmpdir = fio.tempdir()
+    local new_path = fio.pathjoin(tmpdir, script_name)
+    fio.copyfile(path_to_script, new_path)
+    test:is(os.execute(shell_command(set, i)), 0, 'set '..i)
+end
+os.exit(0)
diff --git a/test/box-tap/gh-5602_script.lua b/test/box-tap/gh-5602_script.lua
new file mode 100755
index 000000000..8ff34deb2
--- /dev/null
+++ b/test/box-tap/gh-5602_script.lua
@@ -0,0 +1,70 @@
+local tap = require('tap')
+local test = tap.test('gh-5602')
+
+local status, err = pcall(box.cfg, {background = false, vinyl_timeout = 70.1})
+
+-- Check that environment cfg values are set correctly.
+if arg[1] == '1' then
+    test:plan(5)
+    test:ok(status ~= false, 'box.cfg is successful')
+    test:is(box.cfg['listen'], '3301', 'listen')
+    test:is(box.cfg['readahead'], 10000, 'readahead')
+    test:is(box.cfg['strip_core'], false, 'strip_core')
+    test:is(box.cfg['log_format'], 'json', 'log_format is not set')
+end
+if arg[1] == '2' then
+    test:plan(7)
+    test:ok(status ~= false, 'box.cfg is successful')
+    test:is(box.cfg['listen'], '3301', 'listen')
+    local replication = box.cfg['replication']
+    test:is(type(replication), 'table', 'replication is table')
+    test:ok(replication[1] == '0.0.0.0:12345' or
+            replication[1] == '1.1.1.1:12345', 'replication URI 1')
+    test:ok(replication[2] == '0.0.0.0:12345' or
+            replication[2] == '1.1.1.1:12345', 'replication URI 2')
+    test:is(box.cfg['replication_connect_timeout'], 0.01,
+            'replication_connect_timeout')
+    test:is(box.cfg['replication_synchro_quorum'], '4 + 1',
+            'replication_synchro_quorum')
+end
+
+-- Check that box.cfg{} values are more prioritized than
+-- environment cfg values.
+if arg[1] == '3' then
+    test:plan(3)
+    test:ok(status ~= false, 'box.cfg is successful')
+    test:is(box.cfg['background'], false,
+            'box.cfg{} background value is prioritized')
+    test:is(box.cfg['vinyl_timeout'], 70.1,
+            'box.cfg{} vinyl_timeout value is prioritized')
+end
+
+-- Check bad environment cfg values.
+if arg[1] == '4' then
+    test:plan(2)
+    test:ok(status == false, 'box.cfg is not successful')
+    local index = string.find(err, 'Environment variable TT_SQL_CACHE_SIZE '..
+        'has incorrect value for option \'sql_cache_size\': should be '..
+        'converted to number')
+    test:ok(index ~= nil, 'bad sql_cache_size value')
+end
+if arg[1] == '5' then
+    test:plan(2)
+    test:ok(status == false, 'box.cfg is not successful')
+    print(err)
+    local index = string.find(err, 'Environment variable TT_STRIP_CORE has '..
+        'incorrect value for option \'strip_core\': should be \'true\' '..
+        'or \'false\'')
+    test:ok(index ~= nil, 'bad strip_core value')
+end
+if arg[1] == '6' then
+    test:plan(2)
+    test:ok(status == false, 'box.cfg is not successful')
+    print(err)
+    local index = string.find(err, 'Environment variable TT_REPLICATION has '..
+        'incorrect value for option \'replication\': elements of array should'..
+        ' be quoted strings')
+    test:ok(index ~= nil, 'bad replication value')
+end
+
+os.exit(test:check() and 0 or 1)
\ No newline at end of file
-- 
2.24.3 (Apple Git-128)


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2021-04-13 12:47 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-03-14 15:34 [Tarantool-patches] [PATCH] box: set cfg via environment variables Roman Khabibov via Tarantool-patches
2021-03-19 12:38 ` Leonid Vasiliev via Tarantool-patches
2021-04-09  8:56   ` Roman Khabibov via Tarantool-patches
2021-04-12  8:37     ` Leonid Vasiliev via Tarantool-patches
2021-04-13 12:47       ` Roman Khabibov via Tarantool-patches
2021-04-12  9:52     ` Leonid Vasiliev via Tarantool-patches
2021-03-19 13:23 ` Leonid Vasiliev via Tarantool-patches

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox