Skip to content

Commit d4db96a

Browse files
author
Oleg Chaplashkin
committed
Adapt the server to use the declarative config
You can use a declarative config file to configure the server instance via following parameters: - @string[opt] config_file Declarative YAML configuration for server instance. - @tab[opt] remote_config If `config_file` is not passed, this config value is used to deduce the advertise URI to connect net.box to the instance. Close #367
1 parent 82e8777 commit d4db96a

File tree

3 files changed

+155
-0
lines changed

3 files changed

+155
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
- Add `--list-test-cases` and `--run-test-case` CLI options.
1515
- Introduce preloaded hooks (gh-380).
1616
- Add `treegen` helper as a tree generator (gh-364).
17+
- Add support declarative configuration in `server.lua` (gh-367).
1718

1819
## 1.0.1
1920

luatest/server.lua

+132
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ local fio = require('fio')
1010
local fun = require('fun')
1111
local http_client = require('http.client')
1212
local json = require('json')
13+
local yaml = require('yaml')
1314
local net_box = require('net.box')
1415
local tarantool = require('tarantool')
1516
local uri = require('uri')
@@ -39,6 +40,9 @@ local Server = {
3940
args = '?table',
4041
box_cfg = '?table',
4142

43+
config_file = '?string',
44+
remote_config = '?table',
45+
4246
http_port = '?number',
4347
net_box_port = '?number',
4448
net_box_uri = '?string|table',
@@ -93,6 +97,10 @@ end
9397
-- `net.box` connection to the new server.
9498
-- @tab[opt] object.box_cfg Extra options for `box.cfg()` and the value of the
9599
-- `TARANTOOL_BOX_CFG` env variable which is passed into the server process.
100+
-- @string[opt] object.config_file Declarative YAML configuration for server
101+
-- instance. Used to deduce advertise URI to connect net.box to the instance.
102+
-- @tab[opt] object.remote_config If `config_file` is not passed, this config
103+
-- value is used to deduce the advertise URI to connect net.box to the instance.
96104
-- @tab[opt] extra Table with extra properties for the server object.
97105
-- @return table
98106
function Server:new(object, extra)
@@ -127,8 +135,115 @@ function Server:new(object, extra)
127135
return object
128136
end
129137

138+
-- Determine advertise URI for given instance from a cluster
139+
-- configuration.
140+
local function find_advertise_uri(config, instance_name, dir)
141+
local urilib = require('uri')
142+
if config == nil or next(config) == nil then
143+
return nil
144+
end
145+
146+
-- Determine listen and advertise options that are in effect
147+
-- for the given instance.
148+
local advertise
149+
local listen
150+
151+
for _, group in pairs(config.groups or {}) do
152+
for _, replicaset in pairs(group.replicasets or {}) do
153+
local instance = (replicaset.instances or {})[instance_name]
154+
if instance == nil then
155+
break
156+
end
157+
if instance.iproto ~= nil then
158+
if instance.iproto.advertise ~= nil then
159+
advertise = advertise or instance.iproto.advertise.client
160+
end
161+
listen = listen or instance.iproto.listen
162+
end
163+
if replicaset.iproto ~= nil then
164+
if replicaset.iproto.advertise ~= nil then
165+
advertise = advertise or replicaset.iproto.advertise.client
166+
end
167+
listen = listen or replicaset.iproto.listen
168+
end
169+
if group.iproto ~= nil then
170+
if group.iproto.advertise ~= nil then
171+
advertise = advertise or group.iproto.advertise.client
172+
end
173+
listen = listen or group.iproto.listen
174+
end
175+
end
176+
end
177+
178+
if config.iproto ~= nil then
179+
if config.iproto.advertise ~= nil then
180+
advertise = advertise or config.iproto.advertise.client
181+
end
182+
listen = listen or config.iproto.listen
183+
end
184+
185+
local uris
186+
if advertise ~= nil then
187+
uris = {{uri = advertise}}
188+
else
189+
uris = listen
190+
end
191+
-- luacheck: push ignore 431
192+
for _, uri in ipairs(uris or {}) do
193+
uri = table.copy(uri)
194+
uri.uri = uri.uri:gsub('{{ *instance_name *}}', instance_name)
195+
uri.uri = uri.uri:gsub('unix/:%./', ('unix/:%s/'):format(dir))
196+
local u = urilib.parse(uri)
197+
if u.ipv4 ~= '0.0.0.0' and u.ipv6 ~= '::' and u.service ~= '0' then
198+
return uri
199+
end
200+
end
201+
-- luacheck: pop
202+
error('No suitable URI to connect is found')
203+
end
204+
130205
-- Initialize the server object.
131206
function Server:initialize()
207+
if self.config_file ~= nil then
208+
self.command = arg[-1]
209+
210+
self.args = fun.chain(self.args or {}, {
211+
'--name', self.alias
212+
}):totable()
213+
214+
if self.config_file ~= '' then
215+
table.insert(self.args, '--config')
216+
table.insert(self.args, self.config_file)
217+
218+
-- Take into account self.chdir to calculate a config
219+
-- file path.
220+
local config_file_path = utils.pathjoin(self.chdir, self.config_file)
221+
222+
-- Read the provided config file.
223+
local fh, err = fio.open(config_file_path, {'O_RDONLY'})
224+
if fh == nil then
225+
error(('Unable to open file %q: %s'):format(config_file_path,
226+
err))
227+
end
228+
self.config = yaml.decode(fh:read())
229+
fh:close()
230+
end
231+
232+
if self.net_box_uri == nil then
233+
local config = self.config or self.remote_config
234+
235+
-- NB: listen and advertise URIs are relative to
236+
-- process.work_dir, which, in turn, is relative to
237+
-- self.chdir.
238+
local work_dir
239+
if config.process ~= nil and config.process.work_dir ~= nil then
240+
work_dir = config.process.work_dir
241+
end
242+
local dir = utils.pathjoin(self.chdir, work_dir)
243+
self.net_box_uri = find_advertise_uri(config, self.alias, dir)
244+
end
245+
end
246+
132247
if self.alias == nil then
133248
self.alias = DEFAULT_ALIAS
134249
end
@@ -297,6 +412,11 @@ end
297412
function Server:start(opts)
298413
checks('table', {wait_until_ready = '?boolean'})
299414

415+
opts = opts or {}
416+
if self.config_file and opts.wait_until_ready == nil then
417+
opts.wait_until_ready = self.net_box_uri ~= nil
418+
end
419+
300420
self:initialize()
301421

302422
self:make_workdir()
@@ -550,6 +670,18 @@ function Server:connect_net_box()
550670
error(connection.error)
551671
end
552672
self.net_box = connection
673+
674+
if self.config_file ~= nil then
675+
-- Replace the ready condition.
676+
local saved_eval = self.net_box.eval
677+
self.net_box.eval = function(self, expr, args, opts) -- luacheck:ignore 432
678+
if expr == 'return _G.ready' then
679+
expr = "return require('config'):info().status == 'ready' or " ..
680+
"require('config'):info().status == 'check_warnings'"
681+
end
682+
return saved_eval(self, expr, args, opts)
683+
end
684+
end
553685
end
554686

555687
local function is_header_set(headers, name)

luatest/utils.lua

+22
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
local digest = require('digest')
22
local fun = require('fun')
3+
local fio = require('fio')
34
local yaml = require('yaml')
45

56
local utils = {}
@@ -196,4 +197,25 @@ function utils.table_pack(...)
196197
return {n = select('#', ...), ...}
197198
end
198199

200+
-- Join paths in an intuitive way.
201+
-- If a component is nil, it is skipped.
202+
-- If a component is an absolute path, it skips all the previous
203+
-- components.
204+
-- The wrapper is written for two components for simplicity.
205+
function utils.pathjoin(a, b)
206+
-- No first path -- skip it.
207+
if a == nil then
208+
return b
209+
end
210+
-- No second path -- skip it.
211+
if b == nil then
212+
return a
213+
end
214+
-- The absolute path is checked explicitly due to gh-8816.
215+
if b:startswith('/') then
216+
return b
217+
end
218+
return fio.pathjoin(a, b)
219+
end
220+
199221
return utils

0 commit comments

Comments
 (0)