Skip to content

Commit b03d98b

Browse files
committed
cluster: allow opt-out from global hooks
This patch lets tests create clusters that are not managed by the preloaded hooks, so you can keep multiple clusters alive between tests or reuse a single long-lived cluster across the whole test suite. Closes #414
1 parent 373fffc commit b03d98b

File tree

4 files changed

+161
-39
lines changed

4 files changed

+161
-39
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
- Added an option to create `Cluster` objects without global hook
6+
management, allowing tests to keep clusters alive between test runs.
7+
38
## 1.2.1
49

510
- Fixed a bug when `Server:grep_log()` didn't consider the `reset` option.

README.rst

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,39 @@ to server process.
471471
472472
``luatest.Process:start(path, args, env)`` provides low-level interface to run any other application.
473473

474+
``luatest.cluster`` runs a declarative configuration as a set of Tarantool instances.
475+
By default clusters are registered inside the current test group and cleaned up with
476+
preloaded hooks (``auto_cleanup = true``). When you need to reuse the same cluster across
477+
multiple tests or keep several clusters running at once, pass ``auto_cleanup = false`` and
478+
manage the lifecycle manually:
479+
480+
.. code-block:: Lua
481+
482+
local t = require('luatest')
483+
local cluster = require('luatest.cluster')
484+
local cbuilder = require('luatest.cbuilder')
485+
486+
local g = t.group('shared')
487+
488+
g.before_all(function()
489+
local config = cbuilder:new()
490+
:use_group('g-1')
491+
:use_replicaset('rs-1')
492+
:add_instance('instance-1', {})
493+
:config()
494+
495+
g.cluster = cluster:new(config, {}, {auto_cleanup = false})
496+
g.cluster:start()
497+
end)
498+
499+
g.after_all(function()
500+
g.cluster:drop()
501+
end)
502+
503+
g.test_reuses_cluster_between_cases = function()
504+
t.assert_not_equals(g.cluster['instance-1'].process, nil)
505+
end
506+
474507
There are several small helpers for common actions:
475508

476509
.. code-block:: Lua

luatest/cluster.lua

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,12 @@ local Cluster = require('luatest.class').new()
4747
-- Cluster uses custom __index implementation to support
4848
-- getting instances from it using `cluster['i-001']`.
4949
--
50-
-- Thus, we need to change the metatable of the class
51-
-- with a custom __index method.
52-
local mt = getmetatable(Cluster)
50+
-- The metamethod is set on the instance metatable so that multiple
51+
-- cluster objects can co-exist without clobbering shared state on the
52+
-- class table.
53+
local mt = Cluster.mt
5354
mt.__index = function(self, k)
54-
local method = rawget(mt, k)
55+
local method = Cluster[k]
5556
if method ~= nil then
5657
return method
5758
end
@@ -310,17 +311,31 @@ end
310311
-- @tab[opt] server_opts Extra options passed to server:new().
311312
-- @tab[opt] opts Cluster options.
312313
-- @string[opt] opts.dir Specific directory for the cluster.
314+
-- @bool[opt] opts.auto_cleanup Register the cluster in a test group and
315+
-- automatically drop it using hooks (default: true).
313316
-- @return table
314317
function Cluster:new(config, server_opts, opts)
315-
local g = cluster._group
316-
317318
assert(type(config) == 'table')
318319
assert(config._config == nil, "Please provide cbuilder:new():config()")
319-
assert(g._cluster == nil)
320+
321+
opts = opts or {}
322+
local auto_cleanup = opts.auto_cleanup
323+
324+
if auto_cleanup == nil then
325+
auto_cleanup = true
326+
end
327+
328+
assert(type(auto_cleanup) == 'boolean')
329+
330+
local g
331+
if auto_cleanup then
332+
g = cluster._group
333+
assert(g._cluster == nil)
334+
end
320335

321336
-- Prepare a temporary directory and write a configuration
322337
-- file.
323-
local dir = opts and opts.dir or treegen.prepare_directory({}, {})
338+
local dir = opts.dir or treegen.prepare_directory({}, {})
324339
local config_file_rel = 'config.yaml'
325340
local config_file = treegen.write_file(dir, config_file_rel,
326341
yaml.encode(config))
@@ -352,17 +367,20 @@ function Cluster:new(config, server_opts, opts)
352367
server_map[name] = iserver
353368
end
354369

355-
-- Store a cluster object in 'g'.
356-
self._servers = servers
357-
self._server_map = server_map
358-
self._expelled_servers = {}
359-
self._dir = dir
360-
self._config_file_rel = config_file_rel
361-
self._server_opts = server_opts
362-
363-
g._cluster = self
370+
local object = self:from({
371+
_servers = servers,
372+
_server_map = server_map,
373+
_expelled_servers = {},
374+
_dir = dir,
375+
_config_file_rel = config_file_rel,
376+
_server_opts = server_opts,
377+
})
378+
379+
if auto_cleanup then
380+
g._cluster = object
381+
end
364382

365-
return self
383+
return object
366384
end
367385

368386
-- }}} Replicaset management

test/cluster_test.lua

Lines changed: 87 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ local server_opts = {
1919
}
2020
}
2121

22+
local function require_declarative_configuration()
23+
t.run_only_if(utils.version_current_ge_than(3, 0, 0),
24+
[[Declarative configuration works on Tarantool 3.0.0+.
25+
See tarantool/tarantool@13149d65bc9d for details]])
26+
end
27+
2228
local function assert_instance_running(c, instance, replicaset)
2329
local server = c[instance]
2430
t.assert(type(server) == 'table')
@@ -45,9 +51,7 @@ g.test_start_stop = function()
4551
t.assert_equals(server:eval('return box.info.ro'), is_ro)
4652
end
4753

48-
t.run_only_if(utils.version_current_ge_than(3, 0, 0),
49-
[[Declarative configuration works on Tarantool 3.0.0+.
50-
See tarantool/tarantool@13149d65bc9d for details]])
54+
require_declarative_configuration()
5155

5256
local config = cbuilder:new()
5357
:use_group('group-a')
@@ -88,9 +92,7 @@ g.test_start_stop = function()
8892
end
8993

9094
g.test_start_instance = function()
91-
t.run_only_if(utils.version_current_ge_than(3, 0, 0),
92-
[[Declarative configuration works on Tarantool 3.0.0+.
93-
See tarantool/tarantool@13149d65bc9d for details]])
95+
require_declarative_configuration()
9496

9597
t.assert_equals(g.cluster, nil)
9698

@@ -122,10 +124,34 @@ g.test_start_instance = function()
122124
assert_instance_stopped(c, 'i-002')
123125
end
124126

127+
g.test_manual_lifecycle = function()
128+
require_declarative_configuration()
129+
130+
local config = cbuilder:new()
131+
:use_group('cluster')
132+
:use_replicaset('cluster-rs')
133+
:add_instance('cluster-1', {})
134+
:config()
135+
136+
local c1 = cluster:new(config, server_opts, {auto_cleanup = false})
137+
138+
t.assert_equals(g._cluster, nil)
139+
140+
c1:start()
141+
assert_instance_running(c1, 'cluster-1')
142+
c1:drop()
143+
144+
local c2 = cluster:new(config, server_opts, {auto_cleanup = false})
145+
146+
t.assert_equals(g._cluster, nil)
147+
148+
c2:start()
149+
assert_instance_running(c2, 'cluster-1')
150+
c2:drop()
151+
end
152+
125153
g.test_sync = function()
126-
t.run_only_if(utils.version_current_ge_than(3, 0, 0),
127-
[[Declarative configuration works on Tarantool 3.0.0+.
128-
See tarantool/tarantool@13149d65bc9d for details]])
154+
require_declarative_configuration()
129155

130156
t.assert_equals(g._cluster, nil)
131157

@@ -186,9 +212,7 @@ g.test_sync = function()
186212
end
187213

188214
g.test_sync_start_stop = function()
189-
t.run_only_if(utils.version_current_ge_than(3, 0, 0),
190-
[[Declarative configuration works on Tarantool 3.0.0+.
191-
See tarantool/tarantool@13149d65bc9d for details]])
215+
require_declarative_configuration()
192216

193217
t.assert_equals(g._cluster, nil)
194218

@@ -235,9 +259,7 @@ g.test_sync_start_stop = function()
235259
end
236260

237261
g.test_reload = function()
238-
t.run_only_if(utils.version_current_ge_than(3, 0, 0),
239-
[[Declarative configuration works on Tarantool 3.0.0+.
240-
See tarantool/tarantool@13149d65bc9d for details]])
262+
require_declarative_configuration()
241263

242264
local function assert_instance_failover_mode(c, instance, mode)
243265
local server = c._server_map[instance]
@@ -279,9 +301,7 @@ g.test_reload = function()
279301
end
280302

281303
g.test_each = function()
282-
t.run_only_if(utils.version_current_ge_than(3, 0, 0),
283-
[[Declarative configuration works on Tarantool 3.0.0+.
284-
See tarantool/tarantool@13149d65bc9d for details]])
304+
require_declarative_configuration()
285305

286306
local config = cbuilder:new()
287307
:use_group('g-001')
@@ -305,9 +325,7 @@ g.test_each = function()
305325
end
306326

307327
g.test_startup_error = function()
308-
t.run_only_if(utils.version_current_ge_than(3, 0, 0),
309-
[[Declarative configuration works on Tarantool 3.0.0+.
310-
See tarantool/tarantool@13149d65bc9d for details]])
328+
require_declarative_configuration()
311329

312330
local config = cbuilder:new()
313331
:use_group('g-001')
@@ -318,3 +336,51 @@ g.test_startup_error = function()
318336

319337
cluster:startup_error(config, 'No such file')
320338
end
339+
340+
local g_persistent_clusters = t.group('persistent_clusters')
341+
342+
g_persistent_clusters.before_all(function()
343+
require_declarative_configuration()
344+
345+
g_persistent_clusters.instances = {}
346+
g_persistent_clusters.pids = {}
347+
348+
for i = 1, 3 do
349+
local index = tostring(i)
350+
local instance = 'persistent-' .. index .. '-1'
351+
local config = cbuilder:new()
352+
:use_group('persistent-group-' .. index)
353+
:use_replicaset('persistent-rs-' .. index)
354+
:add_instance(instance, {})
355+
:config()
356+
357+
local c = cluster:new(config, server_opts, {auto_cleanup = false})
358+
c:start()
359+
assert_instance_running(c, instance)
360+
361+
g_persistent_clusters.instances[i] = {cluster = c, instance = instance}
362+
g_persistent_clusters.pids[i] = c[instance].process.pid
363+
end
364+
end)
365+
366+
g_persistent_clusters.after_all(function()
367+
for _, cdata in ipairs(g_persistent_clusters.instances or {}) do
368+
cdata.cluster:drop()
369+
end
370+
end)
371+
372+
g_persistent_clusters.test_clusters_survive_between_tests = function()
373+
for _, cdata in ipairs(g_persistent_clusters.instances) do
374+
assert_instance_running(cdata.cluster, cdata.instance)
375+
end
376+
end
377+
378+
g_persistent_clusters.test_clusters_keep_same_process = function()
379+
for i, cdata in ipairs(g_persistent_clusters.instances) do
380+
local server = cdata.cluster[cdata.instance]
381+
382+
t.assert_is_not(server.process, nil)
383+
t.assert_equals(server.process.pid, g_persistent_clusters.pids[i])
384+
assert_instance_running(cdata.cluster, cdata.instance)
385+
end
386+
end

0 commit comments

Comments
 (0)