@@ -12,7 +12,8 @@ local http_client = require('http.client')
1212local json = require (' json' )
1313local net_box = require (' net.box' )
1414local tarantool = require (' tarantool' )
15- local uri = require (' uri' )
15+ local urilib = require (' uri' )
16+ local yaml = require (' yaml' )
1617local _ , luacov_runner = pcall (require , ' luacov.runner' ) -- luacov may not be installed
1718
1819local assertions = require (' luatest.assertions' )
@@ -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' ,
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 a server
101+ -- instance. Used to deduce advertise URI to connect net.box to the instance.
102+ -- The special value '' means running without `--config <...>` CLI option
103+ -- (but still passes `--name <alias>`).
104+ -- @tab[opt] object.remote_config If `config_file` is not passed, this config
105+ -- value is used to deduce advertise URI to connect net.box to the instance.
96106-- @tab[opt] extra Table with extra properties for the server object.
97107-- @return table
98108function Server :new (object , extra )
@@ -127,8 +137,111 @@ function Server:new(object, extra)
127137 return object
128138end
129139
140+ -- Determine advertise URI for given instance from a cluster
141+ -- configuration.
142+ local function find_advertise_uri (config , instance_name , dir )
143+ if config == nil or next (config ) == nil then
144+ return nil
145+ end
146+
147+ -- Determine listen and advertise options that are in effect
148+ -- for the given instance.
149+ local advertise
150+ local listen
151+
152+ for _ , group in pairs (config .groups or {}) do
153+ for _ , replicaset in pairs (group .replicasets or {}) do
154+ local instance = (replicaset .instances or {})[instance_name ]
155+ if instance == nil then
156+ break
157+ end
158+ if instance .iproto ~= nil then
159+ if instance .iproto .advertise ~= nil then
160+ advertise = advertise or instance .iproto .advertise .client
161+ end
162+ listen = listen or instance .iproto .listen
163+ end
164+ if replicaset .iproto ~= nil then
165+ if replicaset .iproto .advertise ~= nil then
166+ advertise = advertise or replicaset .iproto .advertise .client
167+ end
168+ listen = listen or replicaset .iproto .listen
169+ end
170+ if group .iproto ~= nil then
171+ if group .iproto .advertise ~= nil then
172+ advertise = advertise or group .iproto .advertise .client
173+ end
174+ listen = listen or group .iproto .listen
175+ end
176+ end
177+ end
178+
179+ if config .iproto ~= nil then
180+ if config .iproto .advertise ~= nil then
181+ advertise = advertise or config .iproto .advertise .client
182+ end
183+ listen = listen or config .iproto .listen
184+ end
185+
186+ local uris
187+ if advertise ~= nil then
188+ uris = {{uri = advertise }}
189+ else
190+ uris = listen
191+ end
192+ -- luacheck: push ignore 431
193+ for _ , uri in ipairs (uris or {}) do
194+ uri = table .copy (uri )
195+ uri .uri = uri .uri :gsub (' {{ *instance_name *}}' , instance_name )
196+ uri .uri = uri .uri :gsub (' unix/:%./' , (' unix/:%s/' ):format (dir ))
197+ local u = urilib .parse (uri )
198+ if u .ipv4 ~= ' 0.0.0.0' and u .ipv6 ~= ' ::' and u .service ~= ' 0' then
199+ return uri
200+ end
201+ end
202+ -- luacheck: pop
203+ error (' No suitable URI to connect is found' )
204+ end
205+
130206-- Initialize the server object.
131207function Server :initialize ()
208+ if self .config_file ~= nil then
209+ self .command = arg [- 1 ]
210+
211+ self .args = fun .chain (self .args or {}, {' --name' , self .alias }):totable ()
212+
213+ if self .config_file ~= ' ' then
214+ table.insert (self .args , ' --config' )
215+ table.insert (self .args , self .config_file )
216+
217+ -- Take into account self.chdir to calculate a config
218+ -- file path.
219+ local config_file_path = utils .pathjoin (self .chdir , self .config_file )
220+
221+ -- Read the provided config file.
222+ local fh , err = fio .open (config_file_path , {' O_RDONLY' })
223+ if fh == nil then
224+ error ((' Unable to open file %q: %s' ):format (config_file_path , err ))
225+ end
226+ self .config = yaml .decode (fh :read ())
227+ fh :close ()
228+ end
229+
230+ if self .net_box_uri == nil then
231+ local config = self .config or self .remote_config
232+
233+ -- NB: listen and advertise URIs are relative to
234+ -- process.work_dir, which, in turn, is relative to
235+ -- self.chdir.
236+ local work_dir
237+ if config .process ~= nil and config .process .work_dir ~= nil then
238+ work_dir = config .process .work_dir
239+ end
240+ local dir = utils .pathjoin (self .chdir , work_dir )
241+ self .net_box_uri = find_advertise_uri (config , self .alias , dir )
242+ end
243+ end
244+
132245 if self .alias == nil then
133246 self .alias = DEFAULT_ALIAS
134247 end
@@ -157,7 +270,7 @@ function Server:initialize()
157270 self .net_box_uri = ' localhost:' .. self .net_box_port
158271 end
159272 end
160- local parsed_net_box_uri = uri .parse (self .net_box_uri )
273+ local parsed_net_box_uri = urilib .parse (self .net_box_uri )
161274 if parsed_net_box_uri .host == ' unix/' then
162275 -- Linux uses max 108 bytes for Unix domain socket paths, which means a 107 characters
163276 -- string ended by a null terminator. Other systems use 104 bytes and 103 characters strings.
@@ -170,7 +283,7 @@ function Server:initialize()
170283 end
171284 end
172285 if type (self .net_box_uri ) == ' table' then
173- self .net_box_uri = uri .format (parsed_net_box_uri , true )
286+ self .net_box_uri = urilib .format (parsed_net_box_uri , true )
174287 end
175288
176289 self .env = utils .merge (self .env or {}, self :build_env ())
281394--- Make directory for the server's Unix socket.
282395-- Invoked on the server's start.
283396function Server :make_socketdir ()
284- local parsed_net_box_uri = uri .parse (self .net_box_uri )
397+ local parsed_net_box_uri = urilib .parse (self .net_box_uri )
285398 if parsed_net_box_uri .host == ' unix/' then
286399 fio .mktree (fio .dirname (parsed_net_box_uri .service ))
287400 end
@@ -333,10 +446,14 @@ function Server:start(opts)
333446 })
334447
335448 local wait_until_ready
336- if self .coverage_report then
337- wait_until_ready = self .original_command == DEFAULT_INSTANCE
449+ if self .config_file then
450+ wait_until_ready = self .net_box_uri ~= nil
338451 else
339- wait_until_ready = self .command == DEFAULT_INSTANCE
452+ if self .coverage_report then
453+ wait_until_ready = self .original_command == DEFAULT_INSTANCE
454+ else
455+ wait_until_ready = self .command == DEFAULT_INSTANCE
456+ end
340457 end
341458 if opts ~= nil and opts .wait_until_ready ~= nil then
342459 wait_until_ready = opts .wait_until_ready
@@ -499,10 +616,18 @@ end
499616--- Wait until the server is ready after the start.
500617-- A server is considered ready when its `_G.ready` variable becomes `true`.
501618function Server :wait_until_ready ()
619+ local expr
620+ if self .config_file ~= nil then
621+ expr = " return require('config'):info().status == 'ready' or " ..
622+ " require('config'):info().status == 'check_warnings'"
623+ else
624+ expr = ' return _G.ready'
625+ end
626+
502627 wait_for_condition (' server is ready' , self , function ()
503628 local ok , is_ready = pcall (function ()
504629 self :connect_net_box ()
505- return self .net_box :eval (' return _G.ready ' ) == true
630+ return self .net_box :eval (expr ) == true
506631 end )
507632 return ok and is_ready
508633 end )
0 commit comments