Skip to content

Commit 4602010

Browse files
test: support tarantool 3 cluster from config
After this patch, it is possible to start a Tarantool 3.0 cluster from the config for tests. It doesn't yet integrated with common test matrix yet. The tests are basic and shallow since it would be tested against the whole crud test suite in the next commits of the patchset. Part of #415
1 parent 98aa764 commit 4602010

File tree

7 files changed

+1090
-25
lines changed

7 files changed

+1090
-25
lines changed

test/path.lua

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
local fio = require('fio')
2+
3+
local ROOT = fio.dirname(fio.dirname(fio.abspath(package.search('test.path'))))
4+
5+
local LUA_PATH = ROOT .. '/?.lua;' ..
6+
ROOT .. '/?/init.lua;' ..
7+
ROOT .. '/.rocks/share/tarantool/?.lua;' ..
8+
ROOT .. '/.rocks/share/tarantool/?/init.lua'
9+
10+
return {
11+
ROOT = ROOT,
12+
LUA_PATH = LUA_PATH,
13+
}

test/tarantool3_helpers/cluster.lua

Lines changed: 363 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,363 @@
1+
local checks = require('checks')
2+
local json = require('json')
3+
local fun = require('fun')
4+
local yaml = require('yaml')
5+
6+
local t = require('luatest')
7+
8+
local vtest = require('test.vshard_helpers.vtest')
9+
local treegen = require('test.tarantool3_helpers.treegen')
10+
local server_helper = require('test.tarantool3_helpers.server')
11+
local utils = require('test.tarantool3_helpers.utils')
12+
13+
local Cluster = {}
14+
15+
-- Inspired by
16+
-- https://github.com/tarantool/cartridge/blob/b9dc61e61bb85e75b7da7dc8f369867c0d3786c4/cartridge/test-helpers/cluster.lua
17+
-- together with
18+
-- https://github.com/tarantool/tarantool/blob/1a5e3bf3c3badd14ffc37b2b31e47554e4778cde/test/config-luatest/basic_test.lua#L39-L67
19+
20+
function Cluster:inherit(object)
21+
setmetatable(object, self)
22+
self.__index = self
23+
return object
24+
end
25+
26+
function Cluster:new(object)
27+
checks('table', {
28+
config = 'table',
29+
modules = '?table',
30+
env = '?table',
31+
crud_init = '?boolean',
32+
router_wait_until_ready = '?string',
33+
storage_wait_until_ready = '?string',
34+
})
35+
36+
self:inherit(object)
37+
object:initialize()
38+
return object
39+
end
40+
41+
local function write_config(dir, config)
42+
return treegen.write_script(dir, 'config.yaml', yaml.encode(config))
43+
end
44+
45+
function Cluster:initialize()
46+
self.servers = {}
47+
self.dirs = {}
48+
49+
self.treegen = {}
50+
treegen.init(self.treegen)
51+
52+
for _, group in pairs(self.config.groups) do
53+
for _, replicaset in pairs(group.replicasets) do
54+
local is_router = utils.is_replicaset_a_sharding_router(group, replicaset)
55+
local is_storage = utils.is_replicaset_a_sharding_storage(group, replicaset)
56+
57+
for alias, _ in pairs(replicaset.instances) do
58+
local dir = treegen.prepare_directory(self.treegen, {}, {})
59+
60+
local config_file = write_config(dir, self.config)
61+
62+
for name, content in pairs(self.modules or {}) do
63+
treegen.write_script(dir, name .. '.lua', content)
64+
end
65+
66+
local opts = {config_file = config_file, chdir = dir}
67+
68+
local server = server_helper:new(fun.chain(opts, {alias = alias}):tomap())
69+
70+
for k, v in pairs(self.env or {}) do
71+
server.env[k] = v
72+
end
73+
74+
server:set_router_tag(is_router)
75+
server:set_storage_tag(is_storage)
76+
77+
table.insert(self.servers, server)
78+
self.dirs[server] = dir
79+
end
80+
end
81+
end
82+
83+
self.main_server = self.servers[1]
84+
85+
return self
86+
end
87+
88+
function Cluster:set_etalon_bucket_balance()
89+
local masters = {}
90+
local etalon_balance = {}
91+
local replicaset_count = 0
92+
93+
for _, group in pairs(self.config.groups) do
94+
for rs_id, rs in pairs(group.replicasets) do
95+
if not utils.is_replicaset_a_sharding_storage(group, rs) then
96+
goto continue
97+
end
98+
99+
local rs_uuid
100+
if (rs.database or {}).replicaset_uuid ~= nil then
101+
rs_uuid = rs.database.replicaset_uuid
102+
else
103+
rs_uuid = vtest.replicaset_name_to_uuid(rs_id)
104+
end
105+
106+
local leader_id = rs.leader
107+
assert(leader_id ~= nil, "Only explicit leader is supported now.")
108+
109+
masters[rs_uuid] = self:server(leader_id)
110+
111+
local weight = 1 -- Only equal weight is supported now.
112+
113+
etalon_balance[rs_uuid] = {
114+
weight = weight,
115+
}
116+
replicaset_count = replicaset_count + 1
117+
118+
::continue::
119+
end
120+
end
121+
t.assert_not_equals(masters, {}, 'have masters')
122+
123+
local bucket_count = self.config.sharding.bucket_count
124+
vtest.distribute_etalon_buckets(etalon_balance, masters, replicaset_count, bucket_count)
125+
end
126+
127+
function Cluster:method_on_replicaset(method, config_replicaset, func, args)
128+
for alias, _ in pairs(config_replicaset.instances) do
129+
local server = self:server(alias)
130+
server[method](server, func, args)
131+
end
132+
end
133+
134+
function Cluster:exec_on_replicaset(config_replicaset, func, args)
135+
self:method_on_replicaset('exec', config_replicaset, func, args)
136+
end
137+
138+
function Cluster:eval_on_replicaset(config_replicaset, func, args)
139+
self:method_on_replicaset('eval', config_replicaset, func, args)
140+
end
141+
142+
local function bootstrap_vshard_router()
143+
local vshard = require('vshard')
144+
vshard.router.bootstrap()
145+
end
146+
147+
function Cluster:bootstrap_vshard_routers()
148+
for _, group in pairs(self.config.groups) do
149+
for _, rs in pairs(group.replicasets) do
150+
if utils.is_replicaset_a_sharding_router(group, rs) then
151+
self:exec_on_replicaset(rs, bootstrap_vshard_router)
152+
end
153+
end
154+
end
155+
end
156+
157+
local function bootstrap_crud_router()
158+
local crud = require('crud')
159+
crud.init_router()
160+
end
161+
162+
local function bootstrap_crud_storage()
163+
local crud = require('crud')
164+
crud.init_storage{async = true}
165+
end
166+
167+
function Cluster:bootstrap_crud()
168+
for _, group in pairs(self.config.groups) do
169+
for _, rs in pairs(group.replicasets) do
170+
if utils.is_replicaset_a_sharding_router(group, rs) then
171+
self:exec_on_replicaset(rs, bootstrap_crud_router)
172+
end
173+
174+
if utils.is_replicaset_a_sharding_storage(group, rs) then
175+
self:exec_on_replicaset(rs, bootstrap_crud_storage)
176+
end
177+
end
178+
end
179+
end
180+
181+
function Cluster:wait_for_leaders_rw()
182+
for _, group in pairs(self.config.groups) do
183+
for _, rs in pairs(group.replicasets) do
184+
local leader_id = rs.leader
185+
local leader = self:server(leader_id)
186+
187+
leader:wait_for_rw()
188+
end
189+
end
190+
end
191+
192+
function Cluster:start()
193+
for _, server in ipairs(self.servers) do
194+
server:start({wait_until_ready = false})
195+
end
196+
197+
return self:wait_until_ready()
198+
end
199+
200+
function Cluster:wait_until_ready()
201+
for _, server in ipairs(self.servers) do
202+
server:wait_until_ready()
203+
end
204+
205+
for _, server in ipairs(self.servers) do
206+
t.assert_equals(server:eval('return box.info.name'), server.alias)
207+
end
208+
209+
self:wait_for_leaders_rw()
210+
211+
self:bootstrap()
212+
self:wait_until_bootstrap_finished()
213+
214+
return self
215+
end
216+
217+
function Cluster:bootstrap()
218+
self:set_etalon_bucket_balance()
219+
self:bootstrap_vshard_routers()
220+
221+
if self.crud_init then
222+
self:bootstrap_crud()
223+
end
224+
225+
return self
226+
end
227+
228+
function Cluster:wait_until_bootstrap_finished()
229+
if self.crud_init then
230+
self:wait_crud_is_ready_on_cluster()
231+
end
232+
233+
self:wait_modules_are_ready_on_cluster()
234+
235+
return self
236+
end
237+
238+
local function assert_expected_number_of_storages_is_running(router, expected_number)
239+
local res, err = router:call('crud.storage_info')
240+
assert(
241+
err == nil,
242+
('crud is not bootstrapped: error on getting storage info: %s'):format(err)
243+
)
244+
245+
local running_storages = 0
246+
for _, storage in pairs(res) do
247+
if storage.status == 'running' then
248+
running_storages = running_storages + 1
249+
end
250+
end
251+
252+
assert(
253+
running_storages == expected_number,
254+
('crud is not bootstrapped: expected %d running storages, got the following storage info: %s'):format(
255+
expected_number, json.encode(res))
256+
)
257+
258+
return true
259+
end
260+
261+
function Cluster:wait_crud_is_ready_on_cluster()
262+
local router = self:get_router()
263+
local storages_in_topology = self:count_storages()
264+
265+
local WAIT_TIMEOUT = 5
266+
local DELAY = 0.1
267+
t.helpers.retrying(
268+
{timeout = WAIT_TIMEOUT, delay = DELAY},
269+
assert_expected_number_of_storages_is_running,
270+
router, storages_in_topology
271+
)
272+
273+
return self
274+
end
275+
276+
function Cluster:get_router()
277+
for _, server in pairs(self.servers) do
278+
if server:is_router() then
279+
return server
280+
end
281+
end
282+
283+
return nil
284+
end
285+
286+
function Cluster:count_storages()
287+
local storages_in_topology = 0
288+
for _, server in pairs(self.servers) do
289+
if server:is_storage() then
290+
storages_in_topology = storages_in_topology + 1
291+
end
292+
end
293+
294+
return storages_in_topology
295+
end
296+
297+
function Cluster:wait_modules_are_ready_on_cluster()
298+
for _, group in pairs(self.config.groups) do
299+
for _, rs in pairs(group.replicasets) do
300+
if self.router_wait_until_ready ~= nil
301+
and utils.is_replicaset_a_sharding_router(group, rs) then
302+
self:eval_on_replicaset(rs, self.router_wait_until_ready)
303+
end
304+
305+
if self.storage_wait_until_ready ~= nil
306+
and utils.is_replicaset_a_sharding_storage(group, rs) then
307+
self:eval_on_replicaset(rs, self.storage_wait_until_ready)
308+
end
309+
end
310+
end
311+
312+
return self
313+
end
314+
315+
function Cluster:cfg(new_config)
316+
if new_config ~= nil then
317+
self:reload_config(new_config)
318+
end
319+
320+
return table.deepcopy(self.config)
321+
end
322+
323+
function Cluster:reload_config(new_config)
324+
t.assert_equals(new_config.groups, self.config.groups, 'groups reload is not supported yet')
325+
326+
for _, server in ipairs(self.servers) do
327+
write_config(self.dirs[server], new_config)
328+
end
329+
330+
for _, server in ipairs(self.servers) do
331+
server:exec(function()
332+
require('config'):reload()
333+
end)
334+
end
335+
336+
self.config = new_config
337+
end
338+
339+
function Cluster:stop()
340+
for _, server in ipairs(self.servers) do
341+
server:stop()
342+
end
343+
344+
return self
345+
end
346+
347+
function Cluster:drop()
348+
self:stop()
349+
treegen.clean(self.treegen)
350+
351+
return self
352+
end
353+
354+
function Cluster:server(alias)
355+
for _, server in ipairs(self.servers) do
356+
if server.alias == alias then
357+
return server
358+
end
359+
end
360+
error('Server ' .. alias .. ' not found', 2)
361+
end
362+
363+
return Cluster

0 commit comments

Comments
 (0)