-
Notifications
You must be signed in to change notification settings - Fork 26
Expand file tree
/
Copy pathsmoke-test-lib.sh
More file actions
291 lines (261 loc) · 10.9 KB
/
smoke-test-lib.sh
File metadata and controls
291 lines (261 loc) · 10.9 KB
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
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
#!/usr/bin/env bash
# ──────────────────────────────────────────────────────────────────────────────
# Shared library for Devolutions Gateway Linux packaging smoke tests.
# Sourced by smoke-test-deb.sh and smoke-test-rpm.sh.
#
# Expects the following constants to be defined in the sourcing script
# before any check function is called:
# BINARY Path to the gateway binary.
# LIB_DIR Directory containing native libraries.
# LIB_PATH Path to libxmf.so.
# WEBAPP_DIR Path to the webapp root directory.
# CONFIG_DIR Path to the config directory.
# CONFIG_FILE Path to gateway.json.
# UNIT_FILE_PATHS Array of candidate systemd unit file paths (in priority order).
# ──────────────────────────────────────────────────────────────────────────────
# ── Test bookkeeping ──────────────────────────────────────────────────────────
TESTS_PASSED=0
TESTS_FAILED=0
pass() { echo "✅ PASS: $1"; TESTS_PASSED=$((TESTS_PASSED + 1)); }
fail() { echo "❌ FAIL: $1" >&2; TESTS_FAILED=$((TESTS_FAILED + 1)); }
info() { echo "ℹ️ $1"; }
warn() { echo "⚠️ WARN: $1"; }
# ── Summary ───────────────────────────────────────────────────────────────────
summary() {
echo ""
echo "════════════════════════════════════════════════════════════════"
echo " Test Summary: $TESTS_PASSED passed, $TESTS_FAILED failed"
echo "════════════════════════════════════════════════════════════════"
if [ "$TESTS_FAILED" -gt 0 ]; then
exit 1
fi
}
# ── Helpers ───────────────────────────────────────────────────────────────────
# Returns 0 if systemd is running AND the unit file is installed on disk.
systemd_and_unit_available() {
[ -d /run/systemd/system ] && command -v systemctl >/dev/null 2>&1 || return 1
for path in "${UNIT_FILE_PATHS[@]}"; do
[ -f "$path" ] && return 0
done
return 1
}
# ── Check functions ───────────────────────────────────────────────────────────
check_binary_executable() {
if [ -x "$BINARY" ]; then
pass "Main binary exists and is executable: $BINARY"
else
fail "Main binary missing or not executable: $BINARY"
fi
}
check_native_library() {
if [ -f "$LIB_PATH" ] && file "$LIB_PATH" 2>/dev/null | grep -q 'ELF'; then
pass "Native library exists and is a valid ELF: $LIB_PATH"
else
fail "Native library missing or not a valid ELF: $LIB_PATH"
fi
}
check_webapp() {
if [ -d "$WEBAPP_DIR" ]; then
pass "Webapp directory exists: $WEBAPP_DIR"
else
fail "Webapp directory missing: $WEBAPP_DIR"
fi
for app in client player; do
if [ -f "$WEBAPP_DIR/$app/index.html" ]; then
pass "Webapp $app entry point exists: $WEBAPP_DIR/$app/index.html"
else
fail "Webapp $app entry point missing: $WEBAPP_DIR/$app/index.html"
fi
done
}
check_config_dir() {
if [ -d "$CONFIG_DIR" ]; then
pass "Config directory exists: $CONFIG_DIR"
else
fail "Config directory missing: $CONFIG_DIR"
fi
}
check_config_dir_permissions() {
local perms
perms=$(stat -c '%a' "$CONFIG_DIR" 2>/dev/null)
if [ "$perms" = "750" ]; then
pass "Config directory has secure permissions ($perms): $CONFIG_DIR"
else
fail "Config directory has insecure permissions ($perms, expected 750): $CONFIG_DIR"
fi
}
check_binary_help() {
HELP_OUTPUT=$("$BINARY" --help 2>&1) && HELP_RC=$? || HELP_RC=$?
if [ "$HELP_RC" -eq 0 ] || echo "$HELP_OUTPUT" | grep -qi 'gateway\|usage\|help'; then
pass "Binary responds to --help"
else
fail "Binary does not respond to --help (exit code: $HELP_RC)"
fi
}
check_config_init() {
if [ -f "$CONFIG_FILE" ]; then
pass "Default config file exists: $CONFIG_FILE"
if python3 -c "import json; json.load(open('$CONFIG_FILE'))" 2>/dev/null; then
pass "$(basename "$CONFIG_FILE") is valid JSON"
else
fail "$(basename "$CONFIG_FILE") exists but is not valid JSON"
fi
else
fail "Default config file missing after installation: $CONFIG_FILE"
fi
}
# Usage: check_unit_file <fail|warn>
# Searches UNIT_FILE_PATHS in order; on absence, either fails or warns.
check_unit_file() {
local on_absent="$1"
local unit_file=""
for path in "${UNIT_FILE_PATHS[@]}"; do
if [ -f "$path" ]; then
unit_file="$path"
break
fi
done
if [ -n "$unit_file" ]; then
pass "systemd unit file exists: $unit_file"
if grep -q "$BINARY" "$unit_file"; then
pass "Unit file references correct binary path"
else
fail "Unit file does not reference $BINARY"
fi
elif [ "$on_absent" = "fail" ]; then
fail "systemd unit file not found"
else
warn "systemd unit file not found after registration attempt."
info "This is expected in container environments without systemd."
fi
}
check_single_execstart() {
local unit_file="" count
for path in "${UNIT_FILE_PATHS[@]}"; do
if [ -f "$path" ]; then
unit_file="$path"
break
fi
done
if [ -z "$unit_file" ]; then
warn "Skipping ExecStart check: no unit file found (check_unit_file already reported this)."
return
fi
# Match only non-empty ExecStart= lines; bare 'ExecStart=' is a reset directive.
count=$(grep -c '^ExecStart=[^[:space:]]' "$unit_file" 2>/dev/null || true)
if [ "$count" -eq 1 ]; then
pass "Service file has exactly one ExecStart directive"
else
fail "Service file has $count ExecStart directives (expected exactly 1)"
fi
}
check_config_file_permissions() {
if [ ! -f "$CONFIG_FILE" ]; then
fail "Config file not found, cannot check permissions: $CONFIG_FILE"
return
fi
local perms
perms=$(stat -c '%a' "$CONFIG_FILE" 2>/dev/null)
if [ "$perms" = "600" ]; then
pass "Config file has secure permissions ($perms): $CONFIG_FILE"
else
fail "Config file has insecure permissions ($perms, expected 600): $CONFIG_FILE"
fi
}
check_provisioner_key() {
info "Generating RSA-2048 provisioner key pair with openssl…"
KEY_LOG=$(mktemp)
if openssl genrsa -out "$CONFIG_DIR/provisioner.key" 2048 >"$KEY_LOG" 2>&1 \
&& openssl rsa -in "$CONFIG_DIR/provisioner.key" \
-pubout -out "$CONFIG_DIR/provisioner.pem" >>"$KEY_LOG" 2>&1; then
chmod 600 "$CONFIG_DIR/provisioner.key"
pass "Provisioner key pair generated: $CONFIG_DIR/provisioner.pem"
else
echo "openssl output:"
cat "$KEY_LOG"
fail "Failed to generate provisioner key pair"
fi
rm -f "$KEY_LOG"
}
check_service_health() {
local health_url="http://localhost:7171/jet/health"
local gateway_pid=""
local gateway_log=""
if systemd_and_unit_available; then
info "systemd available — using systemctl start/stop"
if ! systemctl start devolutions-gateway >/dev/null 2>&1; then
fail "systemctl start devolutions-gateway failed"
echo "Service logs:"
journalctl -u devolutions-gateway --no-pager -n 50 2>/dev/null || true
return
fi
else
info "systemd not available — starting binary directly"
gateway_log=$(mktemp)
"$BINARY" 2>"$gateway_log" &
gateway_pid=$!
fi
# Wait for the service to be ready (up to 10 s).
local i=0
while [ "$i" -lt 10 ]; do
curl -sf -H 'Accept: application/json' "$health_url" >/dev/null 2>&1 && break
sleep 1
i=$((i + 1))
done
local health_output health_rc
health_output=$(curl -sf -H 'Accept: application/json' "$health_url" 2>/dev/null) && health_rc=$? || health_rc=$?
# Stop the service.
if systemd_and_unit_available; then
systemctl stop devolutions-gateway >/dev/null 2>&1 || true
elif [ -n "$gateway_pid" ]; then
kill "$gateway_pid" 2>/dev/null || true
wait "$gateway_pid" 2>/dev/null || true
fi
if [ "$health_rc" -eq 0 ]; then
pass "Health endpoint responded: $health_output"
# Verify the version field in the health response matches expected.
local health_version
health_version=$(python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('version',''))" <<< "$health_output" 2>/dev/null) || health_version=""
if [ -n "$health_version" ] && echo "$health_version" | grep -qF "$VERSION"; then
pass "Health response version ($health_version) matches expected ($VERSION)"
elif [ -n "$health_version" ]; then
fail "Health response version ($health_version) does not match expected ($VERSION)"
else
warn "Could not extract version from health response"
fi
else
fail "Health endpoint did not respond at $health_url after 10 s"
if systemd_and_unit_available; then
echo "Service logs:"
journalctl -u devolutions-gateway --no-pager -n 50 2>/dev/null || true
elif [ -n "$gateway_log" ] && [ -f "$gateway_log" ]; then
echo "Gateway process output:"
cat "$gateway_log"
fi
fi
[ -n "$gateway_log" ] && rm -f "$gateway_log"
}
check_post_uninstall() {
if [ ! -f "$BINARY" ]; then
pass "Binary removed after uninstall"
else
fail "Binary still present after uninstall: $BINARY"
fi
local unit_file_found=0
for path in "${UNIT_FILE_PATHS[@]}"; do
if [ -f "$path" ]; then
unit_file_found=1
break
fi
done
if [ "$unit_file_found" -eq 0 ]; then
pass "Unit file removed after uninstall"
else
fail "Unit file still present after uninstall"
fi
if [ -d "$CONFIG_DIR" ]; then
pass "Config directory preserved after uninstall: $CONFIG_DIR"
else
fail "Config directory was removed after uninstall (should be preserved)"
fi
}