-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathtest-compare-output.sh
More file actions
executable file
·253 lines (229 loc) · 10.3 KB
/
test-compare-output.sh
File metadata and controls
executable file
·253 lines (229 loc) · 10.3 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
#!/bin/bash
#
# Test script to compare files generated by the old omv-rsnapshot-conf
# shell script vs the new saltstack Jinja2 templates.
#
# Run as root. Both scripts write to the real output paths; results are
# copied to /tmp/rsnapshot-test/{old,new} after each run for diffing.
#
# Usage:
# sudo bash test-compare-output.sh # run both + synthetic jobs, then compare
# sudo bash test-compare-output.sh --no-salt # skip salt run
# sudo bash test-compare-output.sh --no-old # skip old script run
# sudo bash test-compare-output.sh --no-synthetic # skip adding synthetic test jobs
# sudo bash test-compare-output.sh --diff-only # just compare existing captures (no scripts, no synthetic)
#
OLD_SCRIPT=${OLD_SCRIPT:-/srv/plugins/old/omv-rsnapshot-conf}
TEST_DIR=/tmp/rsnapshot-test
OLD_OUT=${TEST_DIR}/old
NEW_OUT=${TEST_DIR}/new
ACTUAL_CONFS_DIR=/var/lib/openmediavault/rsnapshot.d
ACTUAL_CRONSCRIPT=/var/lib/openmediavault/cron.d/rsnapshot
ACTUAL_CRON_D=/etc/cron.d/openmediavault-rsnapshot
RUN_OLD=true
RUN_SALT=true
RUN_SYNTHETIC=true
for arg in "$@"; do
case $arg in
--no-salt) RUN_SALT=false ;;
--no-old) RUN_OLD=false ;;
--diff-only) RUN_OLD=false; RUN_SALT=false; RUN_SYNTHETIC=false ;;
--no-synthetic) RUN_SYNTHETIC=false ;;
esac
done
# ── Synthetic test jobs ──────────────────────────────────────────────────────
# Each entry: "comment|src_sharedfolder_uuid|dest_sharedfolder_uuid"
#
# Shared folders used (verify with: omv-confdbadm read conf.system.sharedfolder)
# varlibdocker uuid=6ed6872f mntentref=79684322 (/) reldirpath=var/lib/docker/
# test uuid=5a21fb10 mntentref=fde7963c (/srv/mergerfs/p1) reldirpath=/
# rsnapshot_source uuid=8a7d47c6 mntentref=1a78cda9 (/srv/dev-disk-...) reldirpath=rsnapshot_source/
# appdata uuid=6291378b mntentref=bbe2e57a (/srv/single/main) reldirpath=compose/appdata/
# archive uuid=a6bd2f78 mntentref=9493283a (/srv/single/archive) reldirpath=archive/
# rsnapshot_dest uuid=8b93dff7 mntentref=1a78cda9 reldirpath=rsnapshot_dest/
# rsnapshot_dest2 uuid=367bb113 mntentref=1a78cda9 reldirpath=rsnapshot_dest2/
SYNTHETIC_TEST_CASES=(
# Root filesystem source — the bug reported by the user (snapshot_root was ///)
"test: root-fs source (mountpoint=/, reldirpath=var/lib/docker/)|6ed6872f-9afd-462a-847e-7b97e6e8bfa5|8b93dff7-793d-4231-81f3-b161dbfe8e01"
# Source shared folder IS the mountpoint (reldirpath=/), no subdirectory
"test: share IS mountpoint (mountpoint=/srv/mergerfs/p1, reldirpath=/)|5a21fb10-62c0-4232-8756-57529e19dc97|8b93dff7-793d-4231-81f3-b161dbfe8e01"
# Normal case: single subdir on a non-root filesystem
"test: normal single subdir on non-root fs|8a7d47c6-83df-444d-8e56-23ac536bf29d|8b93dff7-793d-4231-81f3-b161dbfe8e01"
# Multi-level subdirectory (reldirpath=compose/appdata/)
"test: multi-level subdir (reldirpath=compose/appdata/)|6291378b-0c57-44b9-a933-54b88420d5d3|367bb113-7f3d-492c-a549-8b14d16eab7f"
# Non-root filesystem, subdir, different mountpoint from the main disk
"test: subdir on separate non-root fs (/srv/single/archive)|a6bd2f78-51da-43f0-8029-01195985a02e|8b93dff7-793d-4231-81f3-b161dbfe8e01"
)
SYNTHETIC_UUIDS=()
add_test_job() {
local comment="$1" src="$2" dest="$3"
local params response uuid
# OMV_CONFIGOBJECT_NEW_UUID signals db->set() to INSERT (not update) and assign a real UUID.
# Value from: python3 -c "import openmediavault; print(openmediavault.getenv('OMV_CONFIGOBJECT_NEW_UUID'))"
local NEW_UUID="fa4b1c66-ef79-11e5-87a0-0002b3a176b4"
params=$(printf '{"uuid":"%s","enable":true,"comment":"%s","srcsharedfolderref":"%s","destsharedfolderref":"%s","hourly":6,"daily":7,"weekly":4,"monthly":3,"yearly":0,"numtries":3,"gid":"users","onefs":false,"erroroutputonly":false,"rsyncargs":"","cmd_preexec":"","cmd_postexec":""}' \
"$NEW_UUID" "$comment" "$src" "$dest")
response=$(/usr/sbin/omv-rpc RSnapshot set "$params")
# Success: raw object JSON (no wrapper). Error: {"response":null,"error":{...}}
if echo "$response" | python3 -c "import json,sys; d=json.load(sys.stdin); sys.exit(1 if d.get('error') else 0)" 2>/dev/null; then
uuid=$(echo "$response" | python3 -c "import json,sys; print(json.load(sys.stdin)['uuid'])")
if [ -z "$uuid" ]; then
echo "ERROR: could not parse UUID from response for: $comment" >&2
echo "$response" >&2
return 1
fi
SYNTHETIC_UUIDS+=("$uuid")
echo " Added job $uuid: $comment"
else
echo "ERROR: failed to create test job: $comment" >&2
echo "$response" >&2
return 1
fi
}
setup_test_jobs() {
echo "=== Adding synthetic test jobs to OMV config ==="
for entry in "${SYNTHETIC_TEST_CASES[@]}"; do
IFS='|' read -r comment src dest <<< "$entry"
add_test_job "$comment" "$src" "$dest"
done
echo "Added ${#SYNTHETIC_UUIDS[@]} synthetic job(s)."
echo ""
}
cleanup_test_jobs() {
[ ${#SYNTHETIC_UUIDS[@]} -eq 0 ] && return
echo ""
echo "=== Cleaning up synthetic test jobs ==="
for uuid in "${SYNTHETIC_UUIDS[@]}"; do
if /usr/sbin/omv-rpc RSnapshot delete "{\"uuid\":\"${uuid}\"}" > /dev/null; then
echo " Deleted job $uuid"
else
echo " WARNING: failed to delete job $uuid — remove it manually via the UI" >&2
fi
done
SYNTHETIC_UUIDS=()
}
capture_files() {
local dest="$1"
mkdir -p "${dest}/rsnapshot.d" "${dest}/cron.d"
ls "${ACTUAL_CONFS_DIR}"/rsnapshot-*.conf &>/dev/null && \
cp "${ACTUAL_CONFS_DIR}"/rsnapshot-*.conf "${dest}/rsnapshot.d/"
[ -f "${ACTUAL_CRONSCRIPT}" ] && cp "${ACTUAL_CRONSCRIPT}" "${dest}/cron.d/rsnapshot"
[ -f "${ACTUAL_CRON_D}" ] && cp "${ACTUAL_CRON_D}" "${dest}/cron.d/openmediavault-rsnapshot"
}
run_old() {
echo "=== Running old omv-rsnapshot-conf ==="
if [ ! -f "${OLD_SCRIPT}" ]; then
echo "ERROR: old script not found at ${OLD_SCRIPT}"
exit 1
fi
bash "${OLD_SCRIPT}"
capture_files "${OLD_OUT}"
echo "Captured to ${OLD_OUT}"
}
run_salt() {
echo "=== Running omv-salt deploy run rsnapshot ==="
omv-salt deploy run rsnapshot
capture_files "${NEW_OUT}"
echo "Captured to ${NEW_OUT}"
}
compare_files() {
echo ""
echo "======================================================================="
echo " COMPARISON: old vs new"
echo "======================================================================="
local any_diff=false
# Normalize cron entries: strip comments, collapse whitespace, strip leading zeros
# from numeric time fields (old script emits "00", new emits "0")
norm_cron() {
grep -v '^#' "$1" |
awk '{for(i=1;i<=5;i++) if($i~/^[0-9]+$/) $i=$i+0; print}' |
sed 's/[[:space:]]\+/ /g' |
sed '/^[[:space:]]*$/d'
}
# Normalize shell scripts for comparison. The new template intentionally improves
# over the old script in a few cosmetic ways that are not bugs:
# - tabs → spaces indentation
# - unquoted [ $var = x ] → quoted [ "$var" = x ]
# - trailing spaces inside echo strings removed
# - extra blank lines between job blocks
# - double slashes in paths (old script bug, new script is correct)
norm_sh() {
expand "$1" |
sed 's/^[[:space:]]*//' |
sed 's/\[ \$\([a-zA-Z_][a-zA-Z_]*\)/[ "\$\1"/g' |
sed 's/[[:space:]]*"$/"/g' |
sed 's|/\+|/|g' |
grep -v '^[[:space:]]*$'
}
# Normalize conf files: collapse multiple slashes in paths.
norm_conf() {
grep -v "^# rsnapshot-$2\.conf$" "$1" |
sed 's|/\+|/|g'
}
# --- /etc/cron.d/openmediavault-rsnapshot ---
# Only compare the cron entries (not headers — old/new use different comment styles)
echo ""
echo "--- cron.d/openmediavault-rsnapshot (cron entries only) ---"
OLD_CRON="${OLD_OUT}/cron.d/openmediavault-rsnapshot"
NEW_CRON="${NEW_OUT}/cron.d/openmediavault-rsnapshot"
if [ -f "${OLD_CRON}" ] && [ -f "${NEW_CRON}" ]; then
diff <(norm_cron "${OLD_CRON}") <(norm_cron "${NEW_CRON}") \
&& echo " MATCH" || any_diff=true
else
[ -f "${OLD_CRON}" ] || echo " MISSING from old"
[ -f "${NEW_CRON}" ] || echo " MISSING from new"
any_diff=true
fi
# --- /var/lib/openmediavault/cron.d/rsnapshot ---
echo ""
echo "--- cron.d/rsnapshot (dispatch script) ---"
OLD_DISPATCH="${OLD_OUT}/cron.d/rsnapshot"
NEW_DISPATCH="${NEW_OUT}/cron.d/rsnapshot"
if [ -f "${OLD_DISPATCH}" ] && [ -f "${NEW_DISPATCH}" ]; then
diff <(norm_sh "${OLD_DISPATCH}") <(norm_sh "${NEW_DISPATCH}") \
&& echo " MATCH" || any_diff=true
else
[ -f "${OLD_DISPATCH}" ] || echo " MISSING from old"
[ -f "${NEW_DISPATCH}" ] || echo " MISSING from new"
any_diff=true
fi
# --- per-job conf files ---
echo ""
echo "--- rsnapshot.d/rsnapshot-*.conf ---"
ALL_UUIDS=$(
{ ls "${OLD_OUT}/rsnapshot.d/"rsnapshot-*.conf 2>/dev/null
ls "${NEW_OUT}/rsnapshot.d/"rsnapshot-*.conf 2>/dev/null; } |
sed 's|.*/rsnapshot-||; s|\.conf$||' | sort -u
)
for uuid in ${ALL_UUIDS}; do
OLD_CONF="${OLD_OUT}/rsnapshot.d/rsnapshot-${uuid}.conf"
NEW_CONF="${NEW_OUT}/rsnapshot.d/rsnapshot-${uuid}.conf"
echo ""
echo " Job: ${uuid}"
if [ -f "${OLD_CONF}" ] && [ -f "${NEW_CONF}" ]; then
diff <(norm_conf "${OLD_CONF}" "${uuid}") \
<(norm_conf "${NEW_CONF}" "${uuid}") \
&& echo " MATCH" || any_diff=true
else
[ -f "${OLD_CONF}" ] || echo " MISSING from old"
[ -f "${NEW_CONF}" ] || echo " MISSING from new"
any_diff=true
fi
done
echo ""
echo "======================================================================="
if ${any_diff}; then
echo " RESULT: Differences found (see above)"
return 1
else
echo " RESULT: All files match"
fi
}
mkdir -p "${OLD_OUT}" "${NEW_OUT}"
if ${RUN_SYNTHETIC} && ( ${RUN_OLD} || ${RUN_SALT} ); then
trap cleanup_test_jobs EXIT
setup_test_jobs
fi
${RUN_OLD} && run_old
${RUN_SALT} && run_salt
compare_files