feat(clients): add transactional client creation
This commit is contained in:
+440
-4
@@ -11,7 +11,7 @@ source "${PROJECT_DIR}/wg-manager.sh"
|
||||
|
||||
case $# in
|
||||
0) ;;
|
||||
1) case $1 in config|parser|status|list|list-redirected|list-deterministic|transaction|rollback|locking|backups|mutation-preserves-source|secret-modes) ;; *) printf 'Ошибка: неизвестная группа тестов или лишние аргументы\n' >&2; exit 64 ;; esac ;;
|
||||
1) case $1 in config|parser|status|list|list-redirected|list-deterministic|transaction|rollback|locking|backups|mutation-preserves-source|secret-modes|add-client|address-allocation|add-collisions|add-rollback|no-secret-logs|add-golden-config|add-invalid-names) ;; *) printf 'Ошибка: неизвестная группа тестов или лишние аргументы\n' >&2; exit 64 ;; esac ;;
|
||||
*) printf 'Ошибка: неизвестная группа тестов или лишние аргументы\n' >&2; exit 64 ;;
|
||||
esac
|
||||
test_group=${1-}
|
||||
@@ -104,6 +104,31 @@ run_config_tests() {
|
||||
assert_equal "$TEST_ROOT/archive" "$ARCHIVE_DIR" 'путь архива'
|
||||
assert_equal "$TEST_ROOT/manager.lock" "$LOCK_FILE" 'путь lock-файла'
|
||||
|
||||
begin_test 'test hooks require a confined non-symlink root and paths'
|
||||
assert_equal 1 "$(TEST_ROOT=/ WG_MANAGER_TEST_MODE=1 CONFIG_PATH=/tmp/x transaction_test_hooks_enabled >/dev/null 2>&1; printf '%s' "$?")" 'TEST_ROOT=/ отклонён'
|
||||
assert_equal 1 "$(TEST_ROOT=relative WG_MANAGER_TEST_MODE=1 CONFIG_PATH=relative transaction_test_hooks_enabled >/dev/null 2>&1; printf '%s' "$?")" 'относительный TEST_ROOT отклонён'
|
||||
ln -s -- "$TEST_ROOT" "${TEST_ROOT}/root-link"
|
||||
assert_equal 1 "$(TEST_ROOT="${TEST_ROOT}/root-link" WG_MANAGER_TEST_MODE=1 CONFIG_PATH="${TEST_ROOT}/root-link/config" transaction_test_hooks_enabled >/dev/null 2>&1; printf '%s' "$?")" 'symlink TEST_ROOT отклонён'
|
||||
assert_equal 1 "$(TEST_ROOT="$TEST_ROOT" WG_MANAGER_TEST_MODE=1 CONFIG_PATH=/tmp/outside transaction_test_hooks_enabled >/dev/null 2>&1; printf '%s' "$?")" 'CONFIG_PATH вне root отклонён'
|
||||
assert_equal 1 "$(TEST_ROOT="$TEST_ROOT" WG_MANAGER_TEST_MODE=1 CONFIG_PATH="$CONFIG_PATH" WG_CONF=/tmp/outside transaction_test_hooks_enabled >/dev/null 2>&1; printf '%s' "$?")" 'WG_CONF вне root отклонён'
|
||||
for hook_path_name in CLIENT_DIR METADATA_DIR BACKUP_DIR LOCK_FILE; do
|
||||
assert_equal 1 "$(TEST_ROOT="$TEST_ROOT" WG_MANAGER_TEST_MODE=1 CONFIG_PATH="$CONFIG_PATH" WG_CONF="$WG_CONF"; declare "$hook_path_name=/tmp/outside"; if transaction_test_hooks_enabled >/dev/null 2>&1; then printf 0; else printf 1; fi)" "${hook_path_name} вне root отклонён"
|
||||
done
|
||||
mkdir -- "${TEST_ROOT}/hook-real"; ln -s -- "${TEST_ROOT}/hook-real" "${TEST_ROOT}/hook-link"
|
||||
for hook_path_name in WG_CONF CLIENT_DIR METADATA_DIR BACKUP_DIR LOCK_FILE; do
|
||||
assert_equal 1 "$(TEST_ROOT="$TEST_ROOT" WG_MANAGER_TEST_MODE=1 CONFIG_PATH="$CONFIG_PATH"; declare "$hook_path_name=${TEST_ROOT}/hook-link/value"; if transaction_test_hooks_enabled >/dev/null 2>&1; then printf 0; else printf 1; fi)" "${hook_path_name} с symlink-компонентом отклонён"
|
||||
done
|
||||
rm -rf -- "${TEST_ROOT}/hook-real" "${TEST_ROOT}/hook-link"
|
||||
mkfifo "${TEST_ROOT}/hook-ready" "${TEST_ROOT}/hook-release"
|
||||
ln -s -- "${TEST_ROOT}/hook-ready" "${TEST_ROOT}/hook-ready-link"
|
||||
printf 'sentinel\n' >"${TEST_ROOT}/hook-pid-sentinel"
|
||||
ln -s -- "${TEST_ROOT}/hook-pid-sentinel" "${TEST_ROOT}/hook-pid-link"
|
||||
assert_equal 1 "$(ADD_PUBLISH_READY_FIFO="${TEST_ROOT}/hook-ready-link" ADD_PUBLISH_RELEASE_FIFO="${TEST_ROOT}/hook-release" WG_MANAGER_TEST_MODE=1; if transaction_test_hooks_enabled >/dev/null 2>&1; then printf 0; else printf 1; fi)" 'symlink publish FIFO отклонён'
|
||||
assert_equal 1 "$(ADD_PUBLISH_READY_FIFO="${TEST_ROOT}/hook-ready" ADD_PUBLISH_RELEASE_FIFO="${TEST_ROOT}/hook-release" ADD_PUBLISH_PID_FILE="${TEST_ROOT}/hook-pid-link" WG_MANAGER_TEST_MODE=1; if transaction_test_hooks_enabled >/dev/null 2>&1; then printf 0; else printf 1; fi)" 'symlink publish PID отклонён'
|
||||
assert_equal 1 "$(ADD_PUBLISH_READY_FIFO="${TEST_ROOT}/hook-ready" ADD_PUBLISH_RELEASE_FIFO="${TEST_ROOT}/hook-release" WG_MANAGER_TEST_MODE=0; if transaction_test_hooks_enabled >/dev/null 2>&1; then printf 0; else printf 1; fi)" 'hooks вне test mode отклонены'
|
||||
rm -f -- "${TEST_ROOT}/hook-ready" "${TEST_ROOT}/hook-release" "${TEST_ROOT}/hook-ready-link" "${TEST_ROOT}/hook-pid-link" "${TEST_ROOT}/hook-pid-sentinel"
|
||||
rm -f -- "${TEST_ROOT}/root-link"
|
||||
|
||||
begin_test 'required keys and parser quoting branches are covered'
|
||||
write_valid_config "${TEST_ROOT}/missing.conf"
|
||||
remove_config_key "${TEST_ROOT}/missing.conf" WG_INTERFACE
|
||||
@@ -154,13 +179,13 @@ run_config_tests() {
|
||||
export WG_MANAGER_ARCHIVE_DIR="${TEST_ROOT}/archive"
|
||||
export WG_MANAGER_LOCK_FILE="${TEST_ROOT}/manager.lock"
|
||||
|
||||
begin_test 'known command validates config then reports not implemented'
|
||||
begin_test 'add without a name reports usage'
|
||||
set +e
|
||||
output=$("${PROJECT_DIR}/wg-manager.sh" add 2>&1)
|
||||
status=$?
|
||||
set -e
|
||||
assert_equal "$EXIT_OPERATION" "$status" 'статус команды-заглушки'
|
||||
assert_contains 'пока не реализована' "$output" 'сообщение заглушки'
|
||||
assert_equal "$EXIT_USAGE" "$status" 'статус add без имени'
|
||||
assert_contains 'Ожидается имя клиента' "$output" 'сообщение add без имени'
|
||||
|
||||
begin_test 'invalid configurations are rejected without execution'
|
||||
marker="${TEST_ROOT}/marker"
|
||||
@@ -233,6 +258,7 @@ run_config_tests() {
|
||||
'nonnumeric_range|START_HOST|abc|Некорректный диапазон'
|
||||
'out_of_range|START_HOST|0|Некорректный диапазон'
|
||||
'invalid_ipv6|IPV6_PREFIX|gg::1|Некорректный IPv6'
|
||||
'invalid_ipv6_prefix_shape|IPV6_PREFIX|2001:db8:ffff:1|Некорректный IPv6-префикс'
|
||||
'triple_ipv6_prefix|IPV6_PREFIX|:::|Некорректный IPv6'
|
||||
'triple_ipv6_dns|CLIENT_DNS|192.0.2.53,:::|Некорректный адрес DNS'
|
||||
'triple_ipv6_endpoint|SERVER_ENDPOINT|:::|Некорректная конечная точка'
|
||||
@@ -802,6 +828,412 @@ transaction_builder_symlink() {
|
||||
ln -s -- "$WG_CONF" "$1"
|
||||
}
|
||||
|
||||
run_m4_tests() {
|
||||
local config_file="${TEST_ROOT}/m4.conf" fixture="${TEST_ROOT}/wg0.conf" runtime_file="${TEST_ROOT}/runtime.dump" status output before
|
||||
cp -- "${PROJECT_DIR}/tests/fixtures/m3/source.conf" "$fixture"
|
||||
write_m2_config "$config_file" "$fixture"
|
||||
export WG_MANAGER_CONFIG="$config_file" WG_MANAGER_BACKUP_DIR="${TEST_ROOT}/backups" WG_MANAGER_LOG_DIR="${TEST_ROOT}/logs" WG_MANAGER_METADATA_DIR="${TEST_ROOT}/metadata" WG_MANAGER_ARCHIVE_DIR="${TEST_ROOT}/archive" WG_MANAGER_LOCK_FILE="${TEST_ROOT}/lock" WG_MANAGER_NOW=1700000100 WG_MANAGER_TEST_MODE=1 WG_MOCK_INTERFACE=wg0
|
||||
printf 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\t51820\t0\nBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=\t(none)\tvpn.example.invalid:51820\t192.0.2.2/32\t0\t0\t0\t0\n' >"$runtime_file"
|
||||
rm -rf -- "${TEST_ROOT}/dump-sequence"
|
||||
mkdir -p "${TEST_ROOT}/dump-sequence"
|
||||
cp -- "$runtime_file" "${TEST_ROOT}/dump-sequence/2"
|
||||
printf 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\t51820\t0\nBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=\t(none)\tvpn.example.invalid:51820\t192.0.2.2/32\t0\t0\t0\t0\nFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF=\t(none)\tvpn.example.invalid:51820\t192.0.2.3/32\t0\t0\t0\t0\n' >"${TEST_ROOT}/dump-sequence/1"
|
||||
export WG_MOCK_DUMP_FILE="$runtime_file" WG_MOCK_DUMP_SEQUENCE_DIR="${TEST_ROOT}/dump-sequence"
|
||||
unset WG_SYNC_FAIL WG_SYNC_FAIL_ONCE_FILE WG_SYNC_FAIL_ON_CALL WG_MOCK_DUMP_FAIL_ON_CALL WG_MOCK_PRIVATE_KEY WG_MOCK_PUBLIC_KEY WG_MOCK_PSK
|
||||
set_runtime_paths
|
||||
load_config
|
||||
rm -rf -- "$METADATA_DIR" "$CLIENT_DIR"
|
||||
mkdir -p "$METADATA_DIR" "$CLIENT_DIR"
|
||||
|
||||
case $test_group in
|
||||
add-invalid-names)
|
||||
begin_test 'invalid names are rejected before key generation'
|
||||
for name in '' 'bad name' '-option' '../path' 'glob*' $'line\nfeed' 'кириллица'; do
|
||||
set +e; output=$(add_client "$name" 2>&1); status=$?; set -e
|
||||
assert_equal "$EXIT_VALIDATION" "$status" "имя отклонено: ${name:-empty}"
|
||||
done
|
||||
assert_equal '' "$(grep -E 'genkey|genpsk|pubkey' "$TEST_MOCK_LOG" || true)" 'генерация не вызвана'
|
||||
set +e; add_client cli --mode full-tunnel --mode split-tunnel >/dev/null 2>&1; status=$?; set -e
|
||||
assert_equal "$EXIT_USAGE" "$status" 'повторный --mode отклонён'
|
||||
set +e; add_client cli --endpoint >/dev/null 2>&1; status=$?; set -e
|
||||
assert_equal "$EXIT_USAGE" "$status" 'отсутствующее значение endpoint отклонено'
|
||||
set +e; add_client cli --unknown value >/dev/null 2>&1; status=$?; set -e
|
||||
assert_equal "$EXIT_USAGE" "$status" 'неизвестный флаг отклонён'
|
||||
set +e; add_client cli --endpoint vpn.example.invalid:51820 >/dev/null 2>&1; status=$?; set -e
|
||||
assert_equal "$EXIT_VALIDATION" "$status" 'endpoint с портом отклонён'
|
||||
return
|
||||
;;
|
||||
''|address-allocation|add-client|add-golden-config|no-secret-logs|secret-modes)
|
||||
begin_test 'fresh CLI add loads configuration before defaults'
|
||||
local fresh_add_script="${TEST_ROOT}/fresh-add.sh" fresh_add_status
|
||||
printf '%s\n' '#!/usr/bin/env bash' 'set -Eeuo pipefail' "cd -- \"$PROJECT_DIR\"" './wg-manager.sh add alice' >"$fresh_add_script"
|
||||
chmod 700 "$fresh_add_script"
|
||||
set +e; /bin/bash "$fresh_add_script" >/dev/null 2>&1; fresh_add_status=$?; set -e
|
||||
assert_equal 0 "$fresh_add_status" 'fresh process add succeeds'
|
||||
assert_file_exists "${CLIENT_DIR}/alice.conf" 'fresh client config'
|
||||
cp -- "${PROJECT_DIR}/tests/fixtures/m3/source.conf" "$fixture"
|
||||
rm -rf -- "$METADATA_DIR" "$CLIENT_DIR"; mkdir -p -- "$METADATA_DIR" "$CLIENT_DIR"
|
||||
rm -f -- "${TEST_ROOT}/dump-sequence/counter"
|
||||
begin_test 'successful add creates isolated client artifacts and peer'
|
||||
: >"$TEST_MOCK_LOG"
|
||||
export WG_MOCK_EXPECTED_PRIVATE_KEY=EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE=
|
||||
if [[ $test_group == no-secret-logs ]]; then
|
||||
local trace_file="${TEST_ROOT}/add-xtrace.log" trace_fd
|
||||
exec {trace_fd}>"$trace_file"
|
||||
BASH_XTRACEFD=$trace_fd
|
||||
set -x
|
||||
set +e; output=$(add_client alice 2>&1); status=$?; set -e
|
||||
set +x
|
||||
exec {trace_fd}>&-
|
||||
else
|
||||
set +e; output=$(add_client alice 2>&1); status=$?; set -e
|
||||
fi
|
||||
unset WG_MOCK_EXPECTED_PRIVATE_KEY
|
||||
assert_equal 0 "$status" 'add succeeds'
|
||||
assert_file_exists "${CLIENT_DIR}/alice.conf" 'client config'
|
||||
assert_file_exists "${CLIENT_DIR}/alice.key" 'client private key'
|
||||
assert_file_exists "${METADATA_DIR}/alice.meta" 'client metadata'
|
||||
assert_file_mode 600 "${CLIENT_DIR}/alice.conf" 'config mode'
|
||||
assert_file_mode 600 "${CLIENT_DIR}/alice.key" 'key mode'
|
||||
assert_file_mode 600 "${CLIENT_DIR}/alice.pub" 'public key mode'
|
||||
assert_file_mode 600 "${CLIENT_DIR}/alice.psk" 'psk mode'
|
||||
assert_file_mode 600 "${METADATA_DIR}/alice.meta" 'metadata mode'
|
||||
assert_file_mode 700 "$CLIENT_DIR" 'client directory mode'
|
||||
assert_file_mode 700 "$METADATA_DIR" 'metadata directory mode'
|
||||
for stage_failure in mkdir chmod meta conf key pub psk; do
|
||||
export TRANSACTION_ADD_DIRECTORY_FAIL=
|
||||
unset TRANSACTION_ADD_STAGE_FAIL
|
||||
if [[ $stage_failure == mkdir || $stage_failure == chmod ]]; then export TRANSACTION_ADD_DIRECTORY_FAIL=$stage_failure; else export TRANSACTION_ADD_STAGE_FAIL=$stage_failure; fi
|
||||
set +e; add_client "fail-${stage_failure//./-}" >/dev/null 2>&1; status=$?; set -e
|
||||
assert_equal "$EXIT_OPERATION" "$status" "предтранзакционный отказ ${stage_failure}"
|
||||
assert_equal 0 "$(find "$CLIENT_DIR" "$METADATA_DIR" -maxdepth 1 -name '.wg-manager.add.*' | wc -l)" "нет stage после ${stage_failure}"
|
||||
unset TRANSACTION_ADD_DIRECTORY_FAIL TRANSACTION_ADD_STAGE_FAIL
|
||||
done
|
||||
assert_contains '192.0.2.3/32' "$(<"${CLIENT_DIR}/alice.conf")" 'allocated address'
|
||||
assert_contains 'PresharedKey = GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG=' "$(<"$fixture")" 'server peer preshared key'
|
||||
assert_equal '' "$(grep -E 'EEEE|FFFF|GGGG|AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=' "$TEST_MOCK_LOG" || true)" 'mock log without key material'
|
||||
local -a add_log_lines=(); mapfile -t add_log_lines <"$TEST_MOCK_LOG"
|
||||
assert_equal 9 "${#add_log_lines[@]}" 'полный порядок add внешних вызовов'
|
||||
assert_equal 1 "$(grep -c '^wg show wg0 public-key$' "$TEST_MOCK_LOG" || true)" 'ровно один server public-key'
|
||||
assert_equal 1 "$(grep -c '^wg genkey$' "$TEST_MOCK_LOG" || true)" 'ровно один genkey'
|
||||
assert_equal 1 "$(grep -c '^wg pubkey$' "$TEST_MOCK_LOG" || true)" 'ровно один pubkey'
|
||||
assert_equal 1 "$(grep -c '^wg genpsk$' "$TEST_MOCK_LOG" || true)" 'ровно один genpsk'
|
||||
assert_equal 1 "$( [[ ${add_log_lines[1]:-} == 'wg show wg0 public-key' ]] && printf 1 || printf 0 )" 'exact server public-key argv'
|
||||
assert_equal 'wg genkey' "${add_log_lines[2]:-}" 'exact genkey argv'
|
||||
assert_equal 'wg pubkey' "${add_log_lines[3]:-}" 'exact pubkey argv'
|
||||
assert_equal 'wg genpsk' "${add_log_lines[4]:-}" 'exact genpsk argv'
|
||||
if [[ $test_group == no-secret-logs ]]; then
|
||||
assert_equal '' "$output" 'add stdout/stderr without secrets'
|
||||
assert_equal '' "$(grep -E 'EEEE|FFFF|GGGG|PrivateKey|PresharedKey|\[Interface\]|\[Peer\]' "$trace_file" || true)" 'xtrace без secret/config values'
|
||||
fi
|
||||
if [[ $test_group == address-allocation ]]; then
|
||||
rm -f -- "${CLIENT_DIR}/alice.conf" "${CLIENT_DIR}/alice.key" "${CLIENT_DIR}/alice.pub" "${CLIENT_DIR}/alice.psk" "${METADATA_DIR}/alice.meta"
|
||||
cp -- "${PROJECT_DIR}/tests/fixtures/m3/source.conf" "$fixture"; rm -f -- "${TEST_ROOT}/dump-sequence/counter"
|
||||
cat >"${METADATA_DIR}/occupied.meta" <<EOF
|
||||
NAME=occupied
|
||||
PUBLIC_KEY=CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC=
|
||||
IPV4=192.0.2.3
|
||||
CREATED_AT=1700000000
|
||||
CONFIG_PATH=${CLIENT_DIR}/occupied.conf
|
||||
CLIENT_MODE=full-tunnel
|
||||
MANAGED=1
|
||||
EOF
|
||||
chmod 600 "${METADATA_DIR}/occupied.meta"
|
||||
sed 's/192\.0\.2\.3\/32/192.0.2.4\/32/' "${TEST_ROOT}/dump-sequence/1" >"${TEST_ROOT}/dump-sequence/address.dump"
|
||||
cp -- "${TEST_ROOT}/dump-sequence/address.dump" "${TEST_ROOT}/dump-sequence/1"
|
||||
set +e; add_client alice >/dev/null 2>&1; local allocation_status=$?; set -e
|
||||
assert_equal 0 "$allocation_status" 'metadata-only address allocation succeeds'
|
||||
assert_contains '192.0.2.4/32' "$(<"${CLIENT_DIR}/alice.conf")" 'metadata address is skipped'
|
||||
rm -f -- "${CLIENT_DIR}/alice.conf" "${CLIENT_DIR}/alice.key" "${CLIENT_DIR}/alice.pub" "${CLIENT_DIR}/alice.psk" "${METADATA_DIR}/alice.meta" "${METADATA_DIR}/occupied.meta"
|
||||
cp -- "${PROJECT_DIR}/tests/fixtures/m3/source.conf" "$fixture"; rm -f -- "${TEST_ROOT}/dump-sequence/counter"
|
||||
replace_config_line "$config_file" 'USE_IPV6=' 'USE_IPV6="1"'
|
||||
sed 's/192\.0\.2\.4\/32/192.0.2.3\/32,2001:db8:ffff::3\/128/' "${TEST_ROOT}/dump-sequence/1" >"${TEST_ROOT}/dump-sequence/ipv6.dump"
|
||||
cp -- "${TEST_ROOT}/dump-sequence/ipv6.dump" "${TEST_ROOT}/dump-sequence/1"
|
||||
set +e; add_client alice >/dev/null 2>&1; allocation_status=$?; set -e
|
||||
assert_equal 0 "$allocation_status" 'ipv6 add succeeds'
|
||||
assert_contains '2001:db8:ffff::3/128' "$(<"${CLIENT_DIR}/alice.conf")" 'client ipv6 address'
|
||||
assert_contains '2001:db8:ffff::3' "$(<"${METADATA_DIR}/alice.meta")" 'metadata ipv6 address'
|
||||
rm -f -- "${CLIENT_DIR}/alice.conf" "${CLIENT_DIR}/alice.key" "${CLIENT_DIR}/alice.pub" "${CLIENT_DIR}/alice.psk" "${METADATA_DIR}/alice.meta" "${METADATA_DIR}/ipv6-occupied.meta"
|
||||
cp -- "${PROJECT_DIR}/tests/fixtures/m3/source.conf" "$fixture"
|
||||
replace_config_line "$config_file" 'START_HOST=' 'START_HOST="10"'
|
||||
replace_config_line "$config_file" 'END_HOST=' 'END_HOST="10"'
|
||||
replace_config_line "$config_file" 'USE_IPV6=' 'USE_IPV6="1"'
|
||||
sed 's/192\.0\.2\.3\/32/192.0.2.10\/32,2001:db8:ffff::a\/128/' "${TEST_ROOT}/dump-sequence/1" >"${TEST_ROOT}/dump-sequence/boundary.dump"
|
||||
cp -- "${TEST_ROOT}/dump-sequence/boundary.dump" "${TEST_ROOT}/dump-sequence/1"
|
||||
load_config; parse_wireguard_config "$fixture"; parse_metadata
|
||||
assert_equal '192.0.2.10/32' "$(allocate_client_ipv4)" 'boundary host 10 allocation'
|
||||
assert_equal '2001:db8:ffff::a/128' "$(allocate_client_ipv6 10)" 'hex host identity a'
|
||||
replace_config_line "$config_file" 'START_HOST=' 'START_HOST="2"'
|
||||
replace_config_line "$config_file" 'END_HOST=' 'END_HOST="2"'
|
||||
replace_config_line "$config_file" 'USE_IPV6=' 'USE_IPV6="1"'
|
||||
rm -f -- "${TEST_ROOT}/dump-sequence/counter" "$TEST_MOCK_LOG"; : >"$TEST_MOCK_LOG"
|
||||
mixed_before=$(sha256sum "$fixture"); local exhaustion_attrs; exhaustion_attrs=$(stat -c '%a:%u:%g' "$fixture")
|
||||
set +e; output=$(add_client exhausted 2>&1); mixed_status=$?; set -e
|
||||
assert_equal "$EXIT_VALIDATION" "$mixed_status" 'IPv4 pool exhaustion rejected'
|
||||
assert_contains 'диапазон IPv4' "$output" 'IPv4 exhaustion diagnostic'
|
||||
assert_equal '' "$(printf '%s' "$output" | grep -E 'EEEE|FFFF|GGGG|PrivateKey|PresharedKey' || true)" 'IPv4 exhaustion diagnostic is secret-free'
|
||||
assert_equal "$mixed_before" "$(sha256sum "$fixture")" 'IPv4 exhaustion preserves source'
|
||||
assert_equal "$exhaustion_attrs" "$(stat -c '%a:%u:%g' "$fixture")" 'IPv4 exhaustion preserves attributes'
|
||||
assert_equal '' "$(grep -E '^wg syncconf |^wg genkey$' "$TEST_MOCK_LOG" || true)" 'IPv4 exhaustion before mutation'
|
||||
for exhausted_artifact in "${METADATA_DIR}/exhausted.meta" "${CLIENT_DIR}/exhausted.conf" "${CLIENT_DIR}/exhausted.key" "${CLIENT_DIR}/exhausted.pub" "${CLIENT_DIR}/exhausted.psk"; do
|
||||
assert_file_absent "$exhausted_artifact" 'IPv4 exhaustion leaves final artifact absent'
|
||||
done
|
||||
assert_equal 0 "$(find "$CLIENT_DIR" "$METADATA_DIR" -maxdepth 1 \( -name '.wg-manager.add.*' -o -name '.wg-manager.key.*' \) | wc -l)" 'IPv4 exhaustion leaves no temps'
|
||||
replace_config_line "$config_file" 'START_HOST=' 'START_HOST="2"'
|
||||
replace_config_line "$config_file" 'END_HOST=' 'END_HOST="254"'
|
||||
replace_config_line "$config_file" 'USE_IPV6=' 'USE_IPV6="1"'
|
||||
rm -f -- "${CLIENT_DIR}/alice.conf" "${CLIENT_DIR}/alice.key" "${CLIENT_DIR}/alice.pub" "${CLIENT_DIR}/alice.psk" "${METADATA_DIR}/alice.meta"
|
||||
cp -- "${PROJECT_DIR}/tests/fixtures/m3/source.conf" "$fixture"
|
||||
sed -i 's/AllowedIPs = 192.0.2.2\/32/AllowedIPs = 192.0.2.2\/32, 2001:db8:ffff::3\/128/' "$fixture"
|
||||
rm -f -- "${TEST_ROOT}/dump-sequence/counter" "$TEST_MOCK_LOG"; : >"$TEST_MOCK_LOG"
|
||||
local mixed_before mixed_status
|
||||
mixed_before=$(sha256sum "$fixture")
|
||||
set +e; add_client mixed-ipv6 >/dev/null 2>&1; mixed_status=$?; set -e
|
||||
assert_equal "$EXIT_VALIDATION" "$mixed_status" 'mixed disk IPv6 collision rejected'
|
||||
assert_equal "$mixed_before" "$(sha256sum "$fixture")" 'mixed IPv6 collision preserves source'
|
||||
assert_equal '' "$(grep -E '^wg syncconf ' "$TEST_MOCK_LOG" || true)" 'mixed IPv6 collision no syncconf'
|
||||
assert_equal '' "$(grep -E '^wg genkey$' "$TEST_MOCK_LOG" || true)" 'mixed IPv6 collision before key generation'
|
||||
cp -- "${PROJECT_DIR}/tests/fixtures/m3/source.conf" "$fixture"
|
||||
cat >"${METADATA_DIR}/ipv6-occupied.meta" <<EOF
|
||||
NAME=ipv6-occupied
|
||||
PUBLIC_KEY=CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC=
|
||||
IPV4=192.0.2.9
|
||||
IPV6=2001:db8:ffff::3
|
||||
CREATED_AT=1700000000
|
||||
CONFIG_PATH=${CLIENT_DIR}/ipv6-occupied.conf
|
||||
CLIENT_MODE=full-tunnel
|
||||
MANAGED=1
|
||||
EOF
|
||||
chmod 600 "${METADATA_DIR}/ipv6-occupied.meta"
|
||||
rm -f -- "${TEST_ROOT}/dump-sequence/counter" "$TEST_MOCK_LOG"; : >"$TEST_MOCK_LOG"
|
||||
mixed_before=$(sha256sum "$fixture")
|
||||
set +e; add_client metadata-ipv6 >/dev/null 2>&1; mixed_status=$?; set -e
|
||||
assert_equal "$EXIT_VALIDATION" "$mixed_status" 'metadata-only IPv6 collision rejected'
|
||||
assert_equal "$mixed_before" "$(sha256sum "$fixture")" 'metadata IPv6 collision preserves source'
|
||||
assert_equal 0 "$(find "$CLIENT_DIR" "$METADATA_DIR" -maxdepth 1 -name '.wg-manager.add.*' | wc -l)" 'no artifacts after IPv6 collision'
|
||||
rm -f -- "${METADATA_DIR}/ipv6-occupied.meta"
|
||||
fi
|
||||
if [[ $test_group == add-golden-config ]]; then
|
||||
assert_equal "$(<"${PROJECT_DIR}/tests/fixtures/m4/golden-server.conf")" "$(<"$fixture")" 'server full golden bytes'
|
||||
assert_equal "$(<"${PROJECT_DIR}/tests/fixtures/m4/golden-client-full.txt")" "$(<"${CLIENT_DIR}/alice.conf")" 'full tunnel golden bytes'
|
||||
assert_equal "$(sed "s|CLIENT_DIR|$CLIENT_DIR|" "${PROJECT_DIR}/tests/fixtures/m4/golden-client.meta.txt")" "$(<"${METADATA_DIR}/alice.meta")" 'metadata golden bytes'
|
||||
rm -f -- "${CLIENT_DIR}/alice.conf" "${CLIENT_DIR}/alice.key" "${CLIENT_DIR}/alice.pub" "${CLIENT_DIR}/alice.psk" "${METADATA_DIR}/alice.meta"
|
||||
cp -- "${PROJECT_DIR}/tests/fixtures/m3/source.conf" "$fixture"; rm -f -- "${TEST_ROOT}/dump-sequence/counter"
|
||||
set +e; add_client alice --mode split-tunnel >/dev/null 2>&1; local split_status=$?; set -e
|
||||
assert_equal 0 "$split_status" 'split tunnel add succeeds'
|
||||
assert_equal "$(<"${PROJECT_DIR}/tests/fixtures/m4/golden-client-split.txt")" "$(<"${CLIENT_DIR}/alice.conf")" 'split tunnel golden bytes'
|
||||
fi
|
||||
return
|
||||
;;
|
||||
add-collisions)
|
||||
begin_test 'name and path collisions are rejected'
|
||||
mkdir -p "$CLIENT_DIR"; printf 'existing\n' >"${CLIENT_DIR}/bob.conf"; chmod 600 "${CLIENT_DIR}/bob.conf"
|
||||
set +e; output=$(add_client bob 2>&1); status=$?; set -e
|
||||
assert_equal "$EXIT_VALIDATION" "$status" 'existing client path collision'
|
||||
assert_contains 'Добавление клиента' "$output" 'existing path diagnostic'
|
||||
assert_equal '' "$(printf '%s' "$output" | grep -E 'EEEE|FFFF|GGGG|PrivateKey|PresharedKey' || true)" 'existing path diagnostic is secret-free'
|
||||
set +e; add_client fixture >/dev/null 2>&1; status=$?; set -e
|
||||
assert_equal "$EXIT_VALIDATION" "$status" 'explicit marker name collision'
|
||||
cat >"${METADATA_DIR}/managed.meta" <<EOF
|
||||
NAME=managed
|
||||
PUBLIC_KEY=CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC=
|
||||
IPV4=192.0.2.3
|
||||
CREATED_AT=1700000000
|
||||
CONFIG_PATH=${CLIENT_DIR}/managed.conf
|
||||
CLIENT_MODE=full-tunnel
|
||||
MANAGED=1
|
||||
EOF
|
||||
chmod 600 "${METADATA_DIR}/managed.meta"
|
||||
set +e; add_client managed >/dev/null 2>&1; status=$?; set -e
|
||||
assert_equal "$EXIT_VALIDATION" "$status" 'metadata name collision'
|
||||
cp -- "${PROJECT_DIR}/tests/fixtures/m3/source.conf" "$fixture"
|
||||
sed -i 's/# wg-manager-client: fixture/### Client legacy/' "$fixture"
|
||||
set +e; add_client legacy >/dev/null 2>&1; status=$?; set -e
|
||||
assert_equal "$EXIT_VALIDATION" "$status" 'legacy marker name collision'
|
||||
for artifact_case in meta conf key pub psk; do
|
||||
rm -rf -- "$METADATA_DIR" "$CLIENT_DIR"; mkdir -p -- "$METADATA_DIR" "$CLIENT_DIR"
|
||||
local artifact_path
|
||||
case $artifact_case in
|
||||
meta) artifact_path="${METADATA_DIR}/collision.meta" ;;
|
||||
*) artifact_path="${CLIENT_DIR}/collision.${artifact_case}" ;;
|
||||
esac
|
||||
printf 'administrator sentinel\n' >"$artifact_path"; chmod 600 "$artifact_path"
|
||||
set +e; output=$(add_client collision 2>&1); status=$?; set -e
|
||||
assert_equal "$EXIT_VALIDATION" "$status" "regular ${artifact_case} collision"
|
||||
assert_equal 1 "$([[ -n $output ]] && printf 1 || printf 0)" "regular ${artifact_case} diagnostic"
|
||||
assert_equal 'administrator sentinel' "$(<"$artifact_path")" "regular ${artifact_case} preserved"
|
||||
rm -f -- "$artifact_path"
|
||||
printf 'outside sentinel\n' >"${TEST_ROOT}/outside-${artifact_case}"
|
||||
ln -s -- "${TEST_ROOT}/outside-${artifact_case}" "$artifact_path"
|
||||
set +e; output=$(add_client collision 2>&1); status=$?; set -e
|
||||
assert_equal "$EXIT_VALIDATION" "$status" "symlink ${artifact_case} collision"
|
||||
assert_equal 1 "$([[ -n $output ]] && printf 1 || printf 0)" "symlink ${artifact_case} diagnostic"
|
||||
assert_equal "${TEST_ROOT}/outside-${artifact_case}" "$(readlink "$artifact_path")" "symlink ${artifact_case} preserved"
|
||||
rm -f -- "$artifact_path" "${TEST_ROOT}/outside-${artifact_case}"
|
||||
done
|
||||
ln -s -- "${TEST_ROOT}/outside" "${CLIENT_DIR}/symlink.conf"
|
||||
set +e; add_client symlink >/dev/null 2>&1; status=$?; set -e
|
||||
assert_equal "$EXIT_VALIDATION" "$status" 'final artifact symlink collision'
|
||||
export WG_MOCK_PUBLIC_KEY=BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=
|
||||
set +e; add_client keycollision >/dev/null 2>&1; status=$?; set -e
|
||||
unset WG_MOCK_PUBLIC_KEY
|
||||
assert_equal "$EXIT_VALIDATION" "$status" 'generated public key collision'
|
||||
cat >"${METADATA_DIR}/key-only.meta" <<EOF
|
||||
NAME=key-only
|
||||
PUBLIC_KEY=HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH=
|
||||
IPV4=192.0.2.9
|
||||
CREATED_AT=1700000000
|
||||
CONFIG_PATH=${CLIENT_DIR}/key-only.conf
|
||||
CLIENT_MODE=full-tunnel
|
||||
MANAGED=1
|
||||
EOF
|
||||
chmod 600 "${METADATA_DIR}/key-only.meta"
|
||||
export WG_MOCK_PUBLIC_KEY=HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH=
|
||||
set +e; add_client metadata-keycollision >/dev/null 2>&1; status=$?; set -e
|
||||
unset WG_MOCK_PUBLIC_KEY
|
||||
assert_equal "$EXIT_VALIDATION" "$status" 'metadata-only public key collision'
|
||||
rm -f -- "${METADATA_DIR}/key-only.meta"
|
||||
for key_case in private-invalid public-invalid psk-invalid; do
|
||||
unset WG_MOCK_PRIVATE_KEY WG_MOCK_PUBLIC_KEY WG_MOCK_PSK
|
||||
case $key_case in
|
||||
private-invalid) export WG_MOCK_PRIVATE_KEY='not-a-key' ;;
|
||||
public-invalid) export WG_MOCK_PUBLIC_KEY='not-a-key' ;;
|
||||
psk-invalid) export WG_MOCK_PSK=$'not-a-key\nextra' ;;
|
||||
esac
|
||||
set +e; output=$(add_client "key-${key_case}" 2>&1); status=$?; set -e
|
||||
assert_equal "$EXIT_OPERATION" "$status" "${key_case} rejected"
|
||||
assert_contains 'Добавление клиента' "$output" "${key_case} diagnostic"
|
||||
assert_equal '' "$(printf '%s' "$output" | grep -E 'EEEE|FFFF|GGGG|PrivateKey|PresharedKey' || true)" "${key_case} diagnostic is secret-free"
|
||||
unset WG_MOCK_PRIVATE_KEY WG_MOCK_PUBLIC_KEY WG_MOCK_PSK
|
||||
done
|
||||
for key_case in server-public-fail server-public-multiline genkey-fail pubkey-fail genpsk-fail private-multiline public-multiline psk-multiline; do
|
||||
unset WG_MOCK_FAIL_SERVER_PUBLIC_KEY WG_MOCK_SERVER_PUBLIC_KEY_OUTPUT WG_MOCK_FAIL_GENKEY WG_MOCK_FAIL_PUBKEY WG_MOCK_FAIL_GENPSK WG_MOCK_PRIVATE_KEY WG_MOCK_PUBLIC_KEY WG_MOCK_PSK
|
||||
case $key_case in
|
||||
server-public-fail) export WG_MOCK_FAIL_SERVER_PUBLIC_KEY=1 ;;
|
||||
server-public-multiline) export WG_MOCK_SERVER_PUBLIC_KEY_OUTPUT=$'DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD=\n' ;;
|
||||
genkey-fail) export WG_MOCK_FAIL_GENKEY=1 ;;
|
||||
pubkey-fail) export WG_MOCK_FAIL_PUBKEY=1 ;;
|
||||
genpsk-fail) export WG_MOCK_FAIL_GENPSK=1 ;;
|
||||
private-multiline) export WG_MOCK_PRIVATE_KEY=$'EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE=\n' ;;
|
||||
public-multiline) export WG_MOCK_PUBLIC_KEY=$'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF=\n' ;;
|
||||
psk-multiline) export WG_MOCK_PSK=$'GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG=\n' ;;
|
||||
esac
|
||||
rm -f -- "${TEST_ROOT}/dump-sequence/counter"
|
||||
local key_before; key_before=$(sha256sum "$fixture")
|
||||
set +e; output=$(add_client "key-${key_case}" 2>&1); status=$?; set -e
|
||||
assert_equal "$EXIT_OPERATION" "$status" "${key_case} rejected"
|
||||
assert_contains 'Добавление клиента' "$output" "${key_case} diagnostic"
|
||||
assert_equal '' "$(printf '%s' "$output" | grep -E 'DDDD|EEEE|FFFF|GGGG|PrivateKey|PresharedKey' || true)" "${key_case} diagnostic is secret-free"
|
||||
assert_equal "$key_before" "$(sha256sum "$fixture")" "${key_case} source preserved"
|
||||
assert_equal '' "$(grep -E '^wg syncconf ' "$TEST_MOCK_LOG" || true)" "${key_case} no syncconf"
|
||||
assert_equal 0 "$(find "$CLIENT_DIR" "$METADATA_DIR" -maxdepth 1 -name '.wg-manager.add.*' | wc -l)" "no artifacts after ${key_case}"
|
||||
assert_equal 0 "$(find "$CLIENT_DIR" "$METADATA_DIR" -maxdepth 1 -name '.wg-manager.key.*' | wc -l)" "no key temp after ${key_case}"
|
||||
unset WG_MOCK_FAIL_SERVER_PUBLIC_KEY WG_MOCK_SERVER_PUBLIC_KEY_OUTPUT WG_MOCK_FAIL_GENKEY WG_MOCK_FAIL_PUBKEY WG_MOCK_FAIL_GENPSK WG_MOCK_PRIVATE_KEY WG_MOCK_PUBLIC_KEY WG_MOCK_PSK
|
||||
done
|
||||
return
|
||||
;;
|
||||
add-rollback)
|
||||
begin_test 'failed apply removes newly generated artifacts'
|
||||
export WG_SYNC_FAIL=1
|
||||
set +e; add_client alice >/dev/null 2>&1; status=$?; set -e
|
||||
unset WG_SYNC_FAIL
|
||||
assert_equal "$EXIT_ROLLBACK" "$status" 'rollback failure is distinct'
|
||||
assert_file_absent "${CLIENT_DIR}/alice.conf" 'config cleaned after failure'
|
||||
assert_file_absent "${METADATA_DIR}/alice.meta" 'metadata cleaned after failure'
|
||||
begin_test 'publish failure rolls back server and runtime'
|
||||
cp -- "${PROJECT_DIR}/tests/fixtures/m3/source.conf" "$fixture"; rm -rf -- "$METADATA_DIR" "$CLIENT_DIR"; mkdir -p -- "$METADATA_DIR" "$CLIENT_DIR"; rm -f -- "${TEST_ROOT}/dump-sequence/counter"
|
||||
before=$(sha256sum "$fixture")
|
||||
export ADD_PUBLISH_FAIL=1
|
||||
set +e; output=$(add_client alice 2>&1); status=$?; set -e
|
||||
unset ADD_PUBLISH_FAIL
|
||||
assert_equal "$EXIT_OPERATION" "$status" 'ошибка публикации откатывается'
|
||||
assert_contains 'Добавление клиента' "$output" 'ошибка публикации сообщает причину'
|
||||
assert_equal '' "$(printf '%s' "$output" | grep -E 'EEEE|FFFF|GGGG|PrivateKey|PresharedKey' || true)" 'ошибка публикации без секретов'
|
||||
assert_equal "$before" "$(sha256sum "$fixture")" 'server restored after publish failure'
|
||||
assert_file_absent "${CLIENT_DIR}/alice.conf" 'publish failure client config absent'
|
||||
assert_file_absent "${METADATA_DIR}/alice.meta" 'publish failure metadata absent'
|
||||
begin_test 'publish race preserves administrator target'
|
||||
rm -f -- "${TEST_ROOT}/dump-sequence/counter"; export ADD_PUBLISH_RACE=1
|
||||
set +e; add_client alice >/dev/null 2>&1; status=$?; set -e
|
||||
unset ADD_PUBLISH_RACE
|
||||
assert_equal "$EXIT_OPERATION" "$status" 'publish race status'
|
||||
assert_equal 'administrator collision' "$(<"${METADATA_DIR}/alice.meta")" 'administrator target preserved'
|
||||
rm -f -- "${METADATA_DIR}/alice.meta"
|
||||
begin_test 'publish rollback failure is critical'
|
||||
rm -f -- "${TEST_ROOT}/dump-sequence/counter"; export ADD_PUBLISH_FAIL=1 TRANSACTION_ROLLBACK_REPLACE_FAIL=1
|
||||
set +e; output=$(add_client alice 2>&1); status=$?; set -e
|
||||
unset ADD_PUBLISH_FAIL TRANSACTION_ROLLBACK_REPLACE_FAIL
|
||||
assert_equal "$EXIT_ROLLBACK" "$status" 'publish rollback failure status'
|
||||
assert_contains 'КРИТИЧНО' "$output" 'publish rollback critical'
|
||||
local publish_env_path; publish_env_path=$(command -v env)
|
||||
for publish_case in 'INT:0:operation' 'TERM:2:operation' 'INT:4:operation' 'TERM:4:operation' 'INT:0:rollback'; do
|
||||
IFS=: read -r publish_signal publish_index publish_outcome <<<"$publish_case"
|
||||
begin_test "${publish_signal} during publish index ${publish_index} is rolled back"
|
||||
cp -- "${PROJECT_DIR}/tests/fixtures/m3/source.conf" "$fixture"
|
||||
rm -rf -- "$METADATA_DIR" "$CLIENT_DIR"; mkdir -p -- "$METADATA_DIR" "$CLIENT_DIR"
|
||||
rm -f -- "${TEST_ROOT}/dump-sequence/counter" "$TEST_MOCK_LOG"
|
||||
: >"$TEST_MOCK_LOG"
|
||||
local ready_fifo="${TEST_ROOT}/publish-${publish_signal}-${publish_index}-${publish_outcome}.ready"
|
||||
local release_fifo="${TEST_ROOT}/publish-${publish_signal}-${publish_index}-${publish_outcome}.release"
|
||||
local pid_file="${TEST_ROOT}/publish-${publish_signal}-${publish_index}-${publish_outcome}.pid"
|
||||
local publish_script="${TEST_ROOT}/publish-${publish_signal}-${publish_index}-${publish_outcome}.sh"
|
||||
local cli_pid transaction_pid publish_status publish_before publish_attrs ready release_fd ready_fd tail_status
|
||||
rm -f -- "$ready_fifo" "$release_fifo" "$pid_file"
|
||||
mkfifo "$ready_fifo" "$release_fifo"
|
||||
exec {ready_fd}<>"$ready_fifo"
|
||||
exec {release_fd}<>"$release_fifo"
|
||||
publish_before=$(sha256sum "$fixture"); publish_attrs=$(stat -c '%a:%u:%g' "$fixture")
|
||||
printf '%s\n' '#!/usr/bin/env bash' 'set -Eeuo pipefail' 'source "$1"' 'set +e' 'add_client alice' 'status=$?' 'exit "$status"' >"$publish_script"
|
||||
chmod 700 "$publish_script"
|
||||
export ADD_PUBLISH_READY_FIFO=$ready_fifo ADD_PUBLISH_RELEASE_FIFO=$release_fifo ADD_PUBLISH_PID_FILE=$pid_file ADD_PUBLISH_READY_INDEX=$publish_index
|
||||
if [[ $publish_outcome == rollback ]]; then export TRANSACTION_ROLLBACK_REPLACE_FAIL=1; else unset TRANSACTION_ROLLBACK_REPLACE_FAIL; fi
|
||||
"$publish_env_path" --default-signal=INT --default-signal=TERM WG_MANAGER_CONFIG="$config_file" WG_MANAGER_BACKUP_DIR="$BACKUP_DIR" WG_MANAGER_METADATA_DIR="$METADATA_DIR" WG_MANAGER_LOCK_FILE="$LOCK_FILE" ADD_PUBLISH_READY_FIFO="$ready_fifo" ADD_PUBLISH_RELEASE_FIFO="$release_fifo" ADD_PUBLISH_PID_FILE="$pid_file" ADD_PUBLISH_READY_INDEX="$publish_index" TRANSACTION_ROLLBACK_REPLACE_FAIL="${TRANSACTION_ROLLBACK_REPLACE_FAIL:-0}" /bin/bash "$publish_script" "$PROJECT_DIR/wg-manager.sh" >"${publish_script}.out" 2>&1 & cli_pid=$!
|
||||
if IFS= read -r -t 10 -u "$ready_fd" ready && [[ $ready == ready ]] && IFS= read -r -t 10 publish_pid <"$pid_file" && [[ $publish_pid =~ ^[0-9]+$ ]]; then
|
||||
transaction_pid=$publish_pid
|
||||
kill -s "$publish_signal" -- "$transaction_pid"
|
||||
printf 'release\n' >&"$release_fd"
|
||||
set +e
|
||||
timeout 10 tail --pid="$cli_pid" -f /dev/null >/dev/null 2>&1; tail_status=$?
|
||||
wait "$cli_pid"; publish_status=$?
|
||||
set -e
|
||||
if [[ $publish_outcome == rollback ]]; then
|
||||
assert_equal "$EXIT_ROLLBACK" "$publish_status" "${publish_signal} publish rollback failure status"
|
||||
assert_contains 'КРИТИЧНО' "$(<"${publish_script}.out")" 'publish rollback failure diagnostic'
|
||||
else
|
||||
assert_equal "$EXIT_OPERATION" "$publish_status" "${publish_signal} publish status"
|
||||
assert_contains "сигналом ${publish_signal}" "$(<"${publish_script}.out")" "${publish_signal} diagnostic"
|
||||
fi
|
||||
assert_equal 0 "$tail_status" "${publish_signal} child completed bounded"
|
||||
if [[ $publish_outcome == operation ]]; then
|
||||
assert_equal "$publish_before" "$(sha256sum "$fixture")" "${publish_signal} source preserved"
|
||||
assert_equal "$publish_attrs" "$(stat -c '%a:%u:%g' "$fixture")" "${publish_signal} attributes preserved"
|
||||
assert_equal 2 "$(grep -c '^wg syncconf wg0 ' "$TEST_MOCK_LOG" || true)" "${publish_signal} syncconf rollback pair"
|
||||
assert_equal 2 "$(grep -c '^wg show wg0 dump$' "$TEST_MOCK_LOG" || true)" "${publish_signal} dump rollback pair"
|
||||
fi
|
||||
for publish_artifact in "${CLIENT_DIR}/alice.conf" "${CLIENT_DIR}/alice.key" "${CLIENT_DIR}/alice.pub" "${CLIENT_DIR}/alice.psk" "${METADATA_DIR}/alice.meta"; do assert_file_absent "$publish_artifact" "${publish_signal} owned artifact cleanup"; done
|
||||
assert_equal 0 "$(find "$(dirname "$fixture")" -maxdepth 1 -name '.wg-manager.add.*' | wc -l)" "${publish_signal} add stages cleanup"
|
||||
unset ADD_PUBLISH_READY_FIFO ADD_PUBLISH_RELEASE_FIFO ADD_PUBLISH_PID_FILE ADD_PUBLISH_READY_INDEX TRANSACTION_ROLLBACK_REPLACE_FAIL
|
||||
rm -f -- "${TEST_ROOT}/dump-sequence/counter" "$TEST_MOCK_LOG"
|
||||
if [[ $publish_outcome == operation ]]; then
|
||||
set +e; add_client after_signal >/dev/null 2>&1; status=$?; set -e
|
||||
assert_equal 0 "$status" "${publish_signal} lock reusable"
|
||||
rm -f -- "${CLIENT_DIR}/after_signal.conf" "${CLIENT_DIR}/after_signal.key" "${CLIENT_DIR}/after_signal.pub" "${CLIENT_DIR}/after_signal.psk" "${METADATA_DIR}/after_signal.meta"
|
||||
fi
|
||||
else
|
||||
kill -TERM "$cli_pid" 2>/dev/null || :
|
||||
printf 'release\n' >&"$release_fd" || :
|
||||
set +e; wait "$cli_pid"; set -e
|
||||
record_failure "${publish_signal} publish synchronization failed"
|
||||
fi
|
||||
exec {ready_fd}>&-
|
||||
exec {release_fd}>&-
|
||||
rm -f -- "$ready_fifo" "$release_fifo" "$pid_file" "$publish_script" "${publish_script}.out"
|
||||
done
|
||||
return
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
run_m3_tests() {
|
||||
local config_file="${TEST_ROOT}/m3.conf" fixture="${TEST_ROOT}/wg0.conf" runtime_file="${TEST_ROOT}/runtime.dump" status output before after backup_count manifest_content parent_exit parent_int parent_term
|
||||
cp -- "${PROJECT_DIR}/tests/fixtures/m2/managed.conf" "$fixture"
|
||||
@@ -1609,6 +2041,9 @@ elif [[ $test_group == parser || $test_group == status || $test_group == list ||
|
||||
run_m2_focused "$test_group"
|
||||
elif [[ $test_group == transaction || $test_group == rollback || $test_group == locking || $test_group == backups || $test_group == mutation-preserves-source || $test_group == secret-modes ]]; then
|
||||
run_m3_tests
|
||||
[[ $test_group == secret-modes ]] && run_m4_tests
|
||||
elif [[ $test_group == add-client || $test_group == address-allocation || $test_group == add-collisions || $test_group == add-rollback || $test_group == no-secret-logs || $test_group == add-golden-config || $test_group == add-invalid-names ]]; then
|
||||
run_m4_tests
|
||||
else
|
||||
run_config_tests
|
||||
run_gum_tests
|
||||
@@ -1616,6 +2051,7 @@ else
|
||||
run_debug_tests
|
||||
run_m2_tests
|
||||
run_m3_tests
|
||||
run_m4_tests
|
||||
fi
|
||||
|
||||
finish_tests
|
||||
|
||||
Reference in New Issue
Block a user