Serialize rule writes and add tests

This commit is contained in:
2026-04-17 10:21:37 +08:00
parent 66c25b06a7
commit eb8c76492c
7 changed files with 682 additions and 35 deletions

View File

@@ -3,9 +3,31 @@ set -euo pipefail
: "${PERSIST_MOCK_LOG:=/tmp/persist-mock.log}"
: "${PERSIST_MOCK_FAIL:=0}"
: "${PERSIST_MOCK_DELAY_SECS:=0}"
: "${PERSIST_MOCK_ACTIVE_DIR:=}"
: "${PERSIST_MOCK_FAIL_ON_CONCURRENT:=0}"
printf '%s %s\n' "$(basename -- "$0")" "$*" >>"${PERSIST_MOCK_LOG}"
cleanup() {
[[ -n ${PERSIST_MOCK_ACTIVE_DIR} ]] || return 0
rmdir "${PERSIST_MOCK_ACTIVE_DIR}" 2>/dev/null || true
}
if [[ -n ${PERSIST_MOCK_ACTIVE_DIR} ]]; then
if ! mkdir "${PERSIST_MOCK_ACTIVE_DIR}" 2>/dev/null; then
if [[ ${PERSIST_MOCK_FAIL_ON_CONCURRENT} == 1 ]]; then
exit 1
fi
else
trap cleanup EXIT
fi
fi
if [[ ${PERSIST_MOCK_DELAY_SECS} != 0 ]]; then
sleep "${PERSIST_MOCK_DELAY_SECS}"
fi
if [[ ${PERSIST_MOCK_FAIL} == 1 ]]; then
exit 1
fi

View File

@@ -13,6 +13,14 @@ status_of() {
printf '%s\n' "${rc}"
}
wait_status() {
local pid=$1
set +e
wait "${pid}"
WAIT_STATUS_RESULT=$?
set -e
}
TMP_DIR=$(mktemp -d)
trap 'rm -rf "${TMP_DIR}"' EXIT
@@ -46,6 +54,7 @@ reset_mock_state() {
: >"${PERSIST_MOCK_LOG}"
unset IPTABLES_MOCK_FAIL_ON_N || true
unset PERSIST_MOCK_FAIL || true
unset PERSIST_MOCK_DELAY_SECS PERSIST_MOCK_ACTIVE_DIR PERSIST_MOCK_FAIL_ON_CONCURRENT || true
storage_init
}
@@ -91,4 +100,33 @@ assert_contains "$(storage_get "${uuid_delete_rollback}")" "uuid=${uuid_delete_r
assert_contains "$(ipt_find_by_uuid "${uuid_delete_rollback}")" "MGMT:${uuid_delete_rollback}" 'persist_save failure on delete should restore runtime rules'
assert_eq '2' "$(grep -Ec 'persist-mock\.sh save' "${PERSIST_MOCK_LOG}")" 'persist_save failure on delete should include add and delete save attempts'
reset_mock_state
uuid_storage_delete_rollback=$(cmd_add_batch tcp 9200 127.0.0.1 93 4 'storage rollback delete')
storage_delete_impl=$(declare -f storage_delete_unlocked)
storage_delete_unlocked() {
return 1
}
assert_eq '1' "$(status_of cmd_delete_uuid "${uuid_storage_delete_rollback}")" 'cmd_delete_uuid should fail when storage delete fails'
assert_eq '1' "$(storage_count)" 'storage delete failure should keep storage record'
assert_contains "$(storage_get "${uuid_storage_delete_rollback}")" "uuid=${uuid_storage_delete_rollback}" 'storage delete failure should keep original storage line'
assert_contains "$(ipt_find_by_uuid "${uuid_storage_delete_rollback}")" "MGMT:${uuid_storage_delete_rollback}" 'storage delete failure should restore runtime rules'
eval "${storage_delete_impl}"
reset_mock_state
export PERSIST_MOCK_DELAY_SECS=0.2
export PERSIST_MOCK_ACTIVE_DIR="${TMP_DIR}/persist-active"
export PERSIST_MOCK_FAIL_ON_CONCURRENT=1
cmd_add_batch tcp 9300 127.0.0.1 94 4 'parallel one' >"${TMP_DIR}/parallel-1.out" 2>"${TMP_DIR}/parallel-1.err" &
pid1=$!
sleep 0.05
cmd_add_batch tcp 9301 127.0.0.1 95 4 'parallel two' >"${TMP_DIR}/parallel-2.out" 2>"${TMP_DIR}/parallel-2.err" &
pid2=$!
wait_status "${pid1}"
assert_eq '0' "${WAIT_STATUS_RESULT}" 'first concurrent add should succeed'
wait_status "${pid2}"
assert_eq '0' "${WAIT_STATUS_RESULT}" 'second concurrent add should wait and succeed'
assert_eq '2' "$(storage_count)" 'concurrent adds should persist both rules'
assert_eq '2' "$(grep -Ec 'persist-mock\.sh save' "${PERSIST_MOCK_LOG}")" 'concurrent adds should serialize persist_save calls'
unset PERSIST_MOCK_DELAY_SECS PERSIST_MOCK_ACTIVE_DIR PERSIST_MOCK_FAIL_ON_CONCURRENT
pass 'test_rules_unit.sh'