From e670f92bc7b80598c3bda20447bcd9f4c6d6e746 Mon Sep 17 00:00:00 2001
From: Mergen Imeev <imeevma@tarantool.org>
Date: Fri, 18 Aug 2023 16:58:40 +0300
Subject: [PATCH] config: move URI compiling instance_config

This patch moves the code that compiles iproto.advertise.peer to
instance_config. This will allow us to use this function for
iproto.advertise.sharding.

Part of #9007

NO_DOC=refactoring
NO_TEST=refactoring
NO_CHANGELOG=refactoring
---
 src/box/lua/config/applier/box_cfg.lua | 115 +----------------------
 src/box/lua/config/instance_config.lua | 121 ++++++++++++++++++++++++-
 test/config-luatest/basic_test.lua     |   2 +-
 3 files changed, 121 insertions(+), 117 deletions(-)

diff --git a/src/box/lua/config/applier/box_cfg.lua b/src/box/lua/config/applier/box_cfg.lua
index c1eb682592..619cf7ed48 100644
--- a/src/box/lua/config/applier/box_cfg.lua
+++ b/src/box/lua/config/applier/box_cfg.lua
@@ -1,121 +1,10 @@
-local urilib = require('uri')
 local fio = require('fio')
 local log = require('internal.config.utils.log')
 local instance_config = require('internal.config.instance_config')
 
--- Accept a comma separated list of URIs and return the first one
--- that is suitable to create a client socket (not just to listen
--- on the server as, say, 0.0.0.0:3301 or localhost:0).
---
--- See the uri_is_suitable_to_connect() method in the instance
--- schema object for details.
-local function find_suitable_uri_to_connect(uris)
-    for _, u in ipairs(urilib.parse_many(uris)) do
-        if instance_config:uri_is_suitable_to_connect(u) then
-            -- The urilib.format() call has the second optional
-            -- argument `write_password`. Let's assume that the
-            -- given URIs are to listen on them and so have no
-            -- user/password.
-            return urilib.format(u)
-        end
-    end
-    return nil
-end
-
-local function find_password(configdata, username)
-    -- The guest user can't have a password.
-    if username == 'guest' then
-        return nil
-    end
-
-    -- Find a user definition in the config.
-    local user_def = configdata:get('credentials.users.' .. username,
-        {use_default = true})
-    if user_def == nil then
-        error(('box_cfg.apply: cannot find user %s in the config to use its ' ..
-            'password in a replication peer URI'):format(username), 0)
-    end
-
-    -- There is a user definition without a password. Let's assume
-    -- that the user has no password.
-    if user_def.password ~= nil then
-        return user_def.password
-    end
-    return nil
-end
-
 local function peer_uri(configdata, peer_name)
-    local opts = {peer = peer_name, use_default = true}
-    local listen = configdata:get('iproto.listen', opts)
-    local advertise = configdata:get('iproto.advertise.peer', opts)
-
-    if advertise ~= nil and not advertise:endswith('@') then
-        -- The iproto.advertise.peer option contains an URI.
-        --
-        -- There are the following cases.
-        --
-        -- * host:port
-        -- * user@host:port
-        -- * user:pass@host:port
-        --
-        -- Note: the host:port part may represent a Unix domain
-        -- socket: host = 'unix/', port = '/path/to/socket'.
-        --
-        -- The second case needs additional handling: we should
-        -- find password for the given user in the 'credential'
-        -- section of the config.
-        --
-        -- Otherwise, the URI is returned as is.
-        local u, err = urilib.parse(advertise)
-        -- NB: The URI is validated, so the parsing can't fail.
-        assert(u ~= nil, err)
-        if u.login ~= nil and u.password == nil then
-            u.password = find_password(configdata, u.login)
-            return urilib.format(u, true)
-        end
-        return advertise
-    elseif listen ~= nil then
-        -- The iproto.advertise.peer option has no URI.
-        --
-        -- There are the following cases.
-        --
-        -- * <no iproto.advertise.peer>
-        -- * user@
-        -- * user:pass@
-        --
-        -- In any case we should find an URI suitable to create a
-        -- client socket in iproto.listen option. After this, add
-        -- the auth information if any.
-        local uri = find_suitable_uri_to_connect(listen)
-        if uri == nil then
-            return nil
-        end
-
-        -- No additional auth information in iproto.advertise.peer:
-        -- return the listen URI as is.
-        if advertise == nil then
-            return uri
-        end
-
-        -- Extract user and password from the iproto.advertise
-        -- option. If no password given, find it in the
-        -- 'credentials' section of the config.
-        assert(advertise:endswith('@'))
-        local auth = advertise:sub(1, -2):split(':', 1)
-        local username = auth[1]
-        local password = auth[2] or find_password(configdata, username)
-
-        -- Rebuild the listen URI with the given username and
-        -- password,
-        local u, err = urilib.parse(uri)
-        -- NB: The URI is validated, so the parsing can't fail.
-        assert(u ~= nil, err)
-        u.login = username
-        u.password = password
-        return urilib.format(u, true)
-    end
-
-    return nil
+    local iconfig = configdata._peers[peer_name].iconfig_def
+    return instance_config:instance_uri(iconfig, 'peer')
 end
 
 local function peer_uris(configdata)
diff --git a/src/box/lua/config/instance_config.lua b/src/box/lua/config/instance_config.lua
index 7d2a86ded0..c0ddfd655f 100644
--- a/src/box/lua/config/instance_config.lua
+++ b/src/box/lua/config/instance_config.lua
@@ -180,7 +180,7 @@ end
 --
 --   It means 'bind to a random free port' for the bind() call,
 --   but it has no meaning at the connect() call on a client.
-local function uri_is_suitable_to_connect(_, uri)
+local function uri_is_suitable_to_connect(uri)
     assert(uri ~= nil)
 
     if uri.ipv4 == '0.0.0.0' then
@@ -222,7 +222,7 @@ local function advertise_peer_uri_validate(data, w)
         end
         w.error('Unable to parse an URI: %s', err)
     end
-    local ok, err = uri_is_suitable_to_connect(nil, uri)
+    local ok, err = uri_is_suitable_to_connect(uri)
     if not ok then
         w.error(err)
     end
@@ -232,6 +232,121 @@ local function advertise_peer_uri_validate(data, w)
     return uri
 end
 
+-- Accept a comma separated list of URIs and return the first one
+-- that is suitable to create a client socket (not just to listen
+-- on the server as, say, 0.0.0.0:3301 or localhost:0).
+--
+-- See the uri_is_suitable_to_connect() method in the instance
+-- schema object for details.
+local function find_suitable_uri_to_connect(uris)
+    for _, u in ipairs(urilib.parse_many(uris)) do
+        if uri_is_suitable_to_connect(u) then
+            -- The urilib.format() call has the second optional
+            -- argument `write_password`. Let's assume that the
+            -- given URIs are to listen on them and so have no
+            -- user/password.
+            return urilib.format(u)
+        end
+    end
+    return nil
+end
+
+local function find_password(self, iconfig, username)
+    -- The guest user can't have a password.
+    if username == 'guest' then
+        return nil
+    end
+
+    -- Find a user definition in the config.
+    local user_def = self:get(iconfig, 'credentials.users.' .. username)
+    if user_def == nil then
+        error(('Cannot find user %s in the config to use its password in a '..
+               'replication peer URI'):format(username), 0)
+    end
+
+    -- There is a user definition without a password. Let's assume
+    -- that the user has no password.
+    if user_def.password ~= nil then
+        return user_def.password
+    end
+    return nil
+end
+
+local function instance_uri(self, iconfig, advertise_type)
+    assert(advertise_type == 'peer' or advertise_type == 'sharding')
+    local listen = self:get(iconfig, 'iproto.listen')
+    local advertise = self:get(iconfig, 'iproto.advertise.'..advertise_type)
+    if advertise == nil and advertise_type == 'sharding' then
+        advertise = self:get(iconfig, 'iproto.advertise.peer')
+    end
+    if advertise ~= nil and not advertise:endswith('@') then
+        -- The iproto.advertise.* option contains an URI.
+        --
+        -- There are the following cases.
+        --
+        -- * host:port
+        -- * user@host:port
+        -- * user:pass@host:port
+        --
+        -- Note: the host:port part may represent a Unix domain
+        -- socket: host = 'unix/', port = '/path/to/socket'.
+        --
+        -- The second case needs additional handling: we should
+        -- find password for the given user in the 'credential'
+        -- section of the config.
+        --
+        -- Otherwise, the URI is returned as is.
+        local u, err = urilib.parse(advertise)
+        -- NB: The URI is validated, so the parsing can't fail.
+        assert(u ~= nil, err)
+        if u.login ~= nil and u.password == nil then
+            u.password = find_password(self, iconfig, u.login)
+            return urilib.format(u, true)
+        end
+        return advertise
+    elseif listen ~= nil then
+        -- The iproto.advertise.* option has no URI.
+        --
+        -- There are the following cases.
+        --
+        -- * <no iproto.advertise.*>
+        -- * user@
+        -- * user:pass@
+        --
+        -- In any case we should find an URI suitable to create a
+        -- client socket in iproto.listen option. After this, add
+        -- the auth information if any.
+        local uri = find_suitable_uri_to_connect(listen)
+        if uri == nil then
+            return nil
+        end
+
+        -- No additional auth information in iproto.advertise.*:
+        -- return the listen URI as is.
+        if advertise == nil then
+            return uri
+        end
+
+        -- Extract user and password from the iproto.advertise
+        -- option. If no password given, find it in the
+        -- 'credentials' section of the config.
+        assert(advertise:endswith('@'))
+        local auth = advertise:sub(1, -2):split(':', 1)
+        local username = auth[1]
+        local password = auth[2] or find_password(self, iconfig, username)
+
+        -- Rebuild the listen URI with the given username and
+        -- password,
+        local u, err = urilib.parse(uri)
+        -- NB: The URI is validated, so the parsing can't fail.
+        assert(u ~= nil, err)
+        u.login = username
+        u.password = password
+        return urilib.format(u, true)
+    end
+    return nil
+end
+
 local function feedback_apply_default_if(_data, _w)
     return box.internal.feedback_daemon ~= nil
 end
@@ -1418,6 +1533,6 @@ return schema.new('instance_config', schema.record({
     config_version = CONFIG_VERSION,
 }), {
     methods = {
-        uri_is_suitable_to_connect = uri_is_suitable_to_connect,
+        instance_uri = instance_uri,
     },
 })
diff --git a/test/config-luatest/basic_test.lua b/test/config-luatest/basic_test.lua
index a7ed62eb48..b4d55396dd 100644
--- a/test/config-luatest/basic_test.lua
+++ b/test/config-luatest/basic_test.lua
@@ -110,7 +110,7 @@ g.test_example_replicaset_election_failover = function(g)
     t.assert_equals(rw_count, 1)
 end
 
-local err_msg_cannot_find_user = 'box_cfg.apply: cannot find user unknown ' ..
+local err_msg_cannot_find_user = 'Cannot find user unknown ' ..
     'in the config to use its password in a replication peer URI'
 local err_msg_no_suitable_uris = 'replication.peers construction for ' ..
     'instance "instance-001" of replicaset "replicaset-001" of group ' ..
-- 
GitLab