Skip to content

Commit

Permalink
config: handle box.cfg's TT_* env vars
Browse files Browse the repository at this point in the history
There may be some confusion, so let's start with a background
information.

There are `TT_*` environment variables introduced in commit 1b33012
("box: set box.cfg options via environment variables"). They're
interpreted by the `box.cfg()` call.

There are `TT_*` environment variables introduced in commit 82b0cff
("config: introduce env source"). They're interpreted by the declarative
configuration logic, when tarantool starts with the `--name <...>` CLI
option.

box.cfg's env variables have names deduced from box.cfg option names,
while config's env variable names are deduced from the config schema.

Some options have the same names here and there, for example
`TT_REPLICATION_ANON` (from `box.cfg.replication_anon` and
`replication.anon`). However, there are ones that have different names,
for example `TT_LISTEN` and `TT_IPROTO_LISTEN`.

Moreover, the declarative configuration has its own restrictions on the
configuration data. For example, `TT_IPROTO_LISTEN` is always a list of
URIs (like `[{"uri": <...>, "params": {<...>}}]`), not a single URI, not
a string, not a number. The declarative configuration has a certain
shape and doesn't allow polymorphic values.

Next, handling of box.cfg's variables by the old code in `load_cfg.lua`
doesn't work well with the declarative configuration flow.

The main reason is that the new configuration flow calls `box.cfg()`
with all the `box.cfg` values set, including default ones. If a user
removes an option from its config, it applies its default. On the same
time it instructs `box.cfg()` to don't read the corresponding
environment variables.

This commit offers a partial solution: it adds support of the most of
the box.cfg environment variables. The values are added into the
configuration data with the lowest priority: if the same value is set
in, for example, a file configuration, the file's value is preferred.

The following box.cfg's environment variables are not handled in this
commit.

* `TT_LOG`
* `TT_METRICS`
* `TT_INSTANCE_NAME`
* `TT_REPLICASET_NAME`
* `TT_CLUSTER_NAME`
* `TT_FORCE_RECOVERY`,
* `TT_READ_ONLY`
* `TT_BOOTSTRAP_LEADER`
* `TT_REPLICATION`
* `TT_REPLICATION_CONNECT_QUORUM`

Fixes tarantool#9485

NO_DOC=looks more like a bug fix or a kind of compatibility layer
  • Loading branch information
Totktonada committed Dec 25, 2023
1 parent 65f2851 commit cd0a95b
Show file tree
Hide file tree
Showing 3 changed files with 387 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## bugfix/config

* Now most of the old `TT_*` environment variables are supported in the new
declarative configuration flow (gh-9485).
76 changes: 76 additions & 0 deletions src/box/lua/config/source/env.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
local uri = require('uri')
local fun = require('fun')
local schema = require('internal.config.utils.schema')
local tabulate = require('internal.config.utils.tabulate')
local instance_config = require('internal.config.instance_config')
Expand All @@ -7,6 +9,62 @@ local mt = {
__index = methods,
}

-- Adjust the given URI list to the form that is accepted by
-- instance config validators.
--
-- The target format is the following.
--
-- {
-- login = <...>,
-- password = <...>,
-- uri = <...>,
-- params = {
-- transport = 'plain',
-- <...>,
-- },
-- }
local function normalize_uri_list(src)
return fun.iter(uri.parse_many(src)):map(function(u)
local res = {
login = u.login,
password = u.password,
}
if u.params ~= nil then
-- If there is only one value with the given key,
-- transform {[k] = {v}} to {[k] = v}.
res.params = fun.iter(u.params):map(function(k, v)
if type(v) == 'table' and #v == 1 then
return k, v[1]
end
return k, v
end):tomap()
end
u = table.copy(u)
u.login = nil
u.password = nil
u.params = nil
res.uri = uri.format(u)
return res
end):totable()
end

local function box_cfg_env_var(box_cfg_option_name)
local res = box.internal.cfg.env[box_cfg_option_name]

if res == nil then
return nil
end

-- TT_LISTEN needs a special handling, because
-- config's schema is more strict regarding a
-- form of an URI than box.cfg's schema.
if box_cfg_option_name == 'listen' then
return normalize_uri_list(res)
end

return res
end

function methods._env_var_name(self, path_in_schema)
local env_var_name = 'TT_' .. table.concat(path_in_schema, '_'):upper()
if self._env_var_suffix ~= nil then
Expand Down Expand Up @@ -75,6 +133,24 @@ end
function methods.sync(self, _config_module, _iconfig)
local values = {}

-- Handle old TT_* box.cfg()'s environment variables such as
-- IPROTO_LISTEN.
--
-- Here we miss all the options without 'box_cfg' annotation:
-- at least 'log', 'replication', 'audit_log' and 'metrics'.
--
-- They can be handled separately if there is a demand.
if self.name == 'env (default)' then
for _, w in instance_config:pairs() do
if w.schema.box_cfg ~= nil then
local value = box_cfg_env_var(w.schema.box_cfg)
if value ~= nil then
instance_config:set(values, w.path, value)
end
end
end
end

for _, w in instance_config:pairs() do
local env_var_name = self:_env_var_name(w.path)
local raw_value = os.getenv(env_var_name)
Expand Down

0 comments on commit cd0a95b

Please sign in to comment.