summaryrefslogtreecommitdiffstats
path: root/daemon/lua/map.test.integr/kresd_config.j2
blob: ae403c702bf14a6d45190aade1f4600d162eb8a8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
-- SPDX-License-Identifier: GPL-3.0-or-later
local ffi = require('ffi')
log_info(ffi.C.LOG_GRP_TESTS, 'my PID = %d', worker.pid)

trust_anchors.remove('.')

cache.size = 2*MB

net = { '{{SELF_ADDR}}' }

{% if QMIN == "false" %}
option('NO_MINIMIZE', true)
{% else %}
option('NO_MINIMIZE', false)
{% endif %}

-- Self-checks on globals
assert(help() ~= nil)
assert(worker.id ~= nil)
-- Self-checks on facilities
assert(cache.count() == 0)
assert(cache.stats() ~= nil)
assert(cache.backends() ~= nil)
assert(worker.stats() ~= nil)
assert(net.interfaces() ~= nil)
-- Self-checks on loaded stuff
assert(#modules.list() > 0)
-- Self-check timers
ev = event.recurrent(1 * sec, function (ev) return 1 end)
event.cancel(ev)

local kluautil = require('kluautil')
local tap = require('tapered')
local checks_total = 16
local n_instances = 3  -- must match deckard.yaml

worker.control_path = worker.cwd .. '/../kresd3/control/'
net.listen(worker.control_path .. worker.pid, nil, {kind = 'control'})
assert(#net.list() >= 3)  -- UDP, TCP, control

-- debug, kept for future use
--log_level("debug")
log_debug(ffi.C.LOG_GRP_TESTS, '%s', worker.control_path)
log_debug(ffi.C.LOG_GRP_TESTS, '%s', table_print(net.list()))

function wait_for_sockets()
	log_info(ffi.C.LOG_GRP_TESTS, 'waiting for control sockets')
	local timeout = 5000 -- ms
	local start_time = tonumber(ffi.C.kr_now())
	local now
	while true do
		now = tonumber(ffi.C.kr_now())
		if now > start_time + timeout then
			log_info(ffi.C.LOG_GRP_TESTS, 'timeout while waiting for control sockets to appear')
			os.exit(3)
		end
		local pids = kluautil.list_dir(worker.control_path)
		if #pids == n_instances then
			-- debug, kept for future use
			log_debug(ffi.C.LOG_GRP_TESTS, 'got control sockets:')
			log_debug(ffi.C.LOG_GRP_TESTS, table_print(pids))
			break
		else
			worker.sleep(0.1)
		end
	end
	log_info(ffi.C.LOG_GRP_TESTS, 'PIDs are visible now (waiting took %d ms)', now - start_time)
end

-- expression should throw Lua error:
-- wrap it in a function which runs the expression on leader and follower
-- separately so we can guarantee both cases are covered
function boom_follower_and_leader(boom_expr, desc)
	local variants = {leader = '~=', follower = '=='}
	for name, operator in pairs(variants) do
		-- beware, newline is not allowed in expr
		local full_expr = string.format(
			'if (worker.pid %s %s) then return true	'
				.. 'else return %s end',
			operator, worker.pid, boom_expr)
		local full_desc = name .. ': '
		if desc then
			full_desc = full_desc .. desc .. ' (' .. boom_expr .. ')'
		else
			full_desc = full_desc .. boom_expr
		end
		tap.boom(map, {full_expr}, full_desc)
	end
end

function tests()
	-- add delay to each test to force scheduler to interleave tests and DNS queries
	local test_delay = 20 / 1000 -- seconds
	log_info(ffi.C.LOG_GRP_TESTS, 'starting map() tests now')

	tap.boom(map, {'1 ++ 1'}, 'syntax error in command is detected')
	worker.sleep(test_delay)

	-- array of integers
	local pids = map('worker.pid')
	tap.same(pids.n, n_instances, 'all pids were obtained')
	table.sort(pids)
	worker.sleep(test_delay)

	-- expression produces array of integers
	local pids_plus_one = map('worker.pid + 1')
	tap.same(pids_plus_one.n, n_instances, 'all pids were obtained')
	table.sort(pids_plus_one)
	for idx=1,n_instances do
		tap.same(pids[idx] + 1, pids_plus_one[idx],
			'increment expression worked')
	end
	worker.sleep(test_delay)

	-- error detection
	boom_follower_and_leader('error("explosion")')
	worker.sleep(test_delay)

	-- unsupported number of return values
	boom_follower_and_leader('1, 2')
	worker.sleep(test_delay)
	boom_follower_and_leader('unpack({})')
	worker.sleep(test_delay)

	-- unsupported return type
	boom_follower_and_leader(
		'function() print("this cannot be serialized") end')
	worker.sleep(test_delay)

	tap.same({n = n_instances}, map('nil'),
		'nil values are counted as returned')
	worker.sleep(test_delay)

	local exp = {n = n_instances}
	for i=1,n_instances do
		table.insert(exp, {nil, 2, nil, n=3})
	end
	local got = map('require("kluautil").kr_table_pack(nil, 2, nil)')
	tap.same(got, exp, 'kr_table_pack handles nil values')
	worker.sleep(test_delay)
end

local started = false
function tests_start()
	-- just in case, duplicates should not happen
	if started then
		log_info(ffi.C.LOG_GRP_TESTS, 'huh? duplicate test invocation ignored, a retransmit?')
		return
	end
	started = true
	log_info(ffi.C.LOG_GRP_TESTS, 'start query triggered, scheduling tests')

	-- DNS queries and map() commands must be serviced while sleep is running
	worker.coroutine(function() worker.sleep(3600) end)

	worker.coroutine(tests)
end
-- Deckard query will trigger tests
policy.add(policy.suffix(tests_start, {'\5start\0'}))

function tests_done()
	print('final query triggered')
	event.after(0, function()
		tap.done(checks_total)
	end)
end
-- Deckard query will execute tap.done() which will call os.exit()
-- i.e. this callback has to be called only after answer to Deckard was sent
policy.add(policy.suffix(tests_done, {'\4done\0'}), true)

-- add delay to each query to force scheduler to interleave tests and DNS queries
policy.add(policy.all(
	function()
		local delay = 10  -- ms
		log_info(ffi.C.LOG_GRP_TESTS, 'packet delayed by %d ms', delay)
		worker.sleep(delay / 1000)
	end))

wait_for_sockets()

{% if DAEMON_NAME == "kresd1" %}

-- forward to Deckard test server
policy.add(policy.all(policy.FORWARD('192.0.2.1')))

{% else %}

-- forward to next kresd instance in chain
{# find out IP address of kresd instance with lower number,
   i.e. kresd2 forwards to kresd1 #}
policy.add(policy.all(policy.FORWARD('{{ PROGRAMS[ "kresd" ~ (DAEMON_NAME[-1]|int() - 1)]["address"] }}')))

{% endif %}