fix(haproxyctl): Prevent config corruption from nested config_foreach
Pre-collect server sections to temp file before iterating backends to avoid UCI state corruption. Nested config_foreach calls were causing server lines to appear in wrong sections (userlist, global). Changes: - Add _precollect_server() to gather all server data before backend iteration - Store server data as pipe-delimited records in temp file - Replace _check_server_sections nested iteration with grep lookup - Replace _add_server_to_backend nested iteration with grep + while read - Mark old _add_server_to_backend as DEPRECATED Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
58758e2824
commit
6b7119d790
@ -951,12 +951,56 @@ backend acme_challenge
|
|||||||
|
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
# PRE-COLLECT ALL SERVER SECTIONS TO AVOID NESTED config_foreach
|
||||||
|
# This prevents UCI state corruption during backend iteration
|
||||||
|
# Bug fix: nested config_foreach calls were interleaving output
|
||||||
|
_servers_data="/tmp/haproxy_servers.$$"
|
||||||
|
rm -f "$_servers_data"
|
||||||
|
|
||||||
|
_precollect_server() {
|
||||||
|
local section="$1"
|
||||||
|
local backend server_name address port weight check enabled
|
||||||
|
config_get backend "$section" backend
|
||||||
|
config_get enabled "$section" enabled "0"
|
||||||
|
[ "$enabled" = "1" ] || return
|
||||||
|
[ -n "$backend" ] || return
|
||||||
|
|
||||||
|
config_get server_name "$section" name "$section"
|
||||||
|
config_get address "$section" address
|
||||||
|
config_get port "$section" port "80"
|
||||||
|
config_get weight "$section" weight "100"
|
||||||
|
config_get check "$section" check "1"
|
||||||
|
|
||||||
|
[ -n "$address" ] || return
|
||||||
|
|
||||||
|
# Resolve hostname to IP if needed
|
||||||
|
if ! echo "$address" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$'; then
|
||||||
|
local resolved_ip=""
|
||||||
|
resolved_ip=$(nslookup "$address" 2>/dev/null | awk '/^Address: / { print $2; exit }')
|
||||||
|
if [ -z "$resolved_ip" ]; then
|
||||||
|
resolved_ip=$(getent hosts "$address" 2>/dev/null | awk '{print $1; exit}')
|
||||||
|
fi
|
||||||
|
if [ -z "$resolved_ip" ]; then
|
||||||
|
log_warn "Cannot resolve hostname '$address' for server $server_name in backend $backend - skipping"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
address="$resolved_ip"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Store in temp file: backend|server_name|address|port|weight|check
|
||||||
|
echo "${backend}|${server_name}|${address}|${port}|${weight}|${check}" >> "$_servers_data"
|
||||||
|
}
|
||||||
|
config_foreach _precollect_server server
|
||||||
|
|
||||||
# Track which backends are generated
|
# Track which backends are generated
|
||||||
_generated_backends=""
|
_generated_backends=""
|
||||||
|
|
||||||
# Generate each backend from UCI
|
# Generate each backend from UCI (now safe - no nested config_foreach needed)
|
||||||
config_foreach _generate_backend backend
|
config_foreach _generate_backend backend
|
||||||
|
|
||||||
|
# Cleanup server data temp file
|
||||||
|
rm -f "$_servers_data"
|
||||||
|
|
||||||
# Collect all backends referenced by vhosts
|
# Collect all backends referenced by vhosts
|
||||||
_referenced_backends=""
|
_referenced_backends=""
|
||||||
_collect_vhost_backend() {
|
_collect_vhost_backend() {
|
||||||
@ -1039,17 +1083,12 @@ _generate_backend() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if there are separate server sections for this backend
|
# Check if there are separate server sections for this backend
|
||||||
|
# NOW USES PRE-COLLECTED DATA instead of nested config_foreach
|
||||||
|
# This avoids UCI state corruption that caused server lines in wrong sections
|
||||||
local has_server_sections=0
|
local has_server_sections=0
|
||||||
_check_server_sections() {
|
if [ -f "$_servers_data" ] && grep -q "^${name}|" "$_servers_data"; then
|
||||||
local srv_section="$1"
|
has_server_sections=1
|
||||||
local srv_backend
|
fi
|
||||||
config_get srv_backend "$srv_section" backend
|
|
||||||
config_get srv_enabled "$srv_section" enabled "0"
|
|
||||||
if [ "$srv_backend" = "$name" ] && [ "$srv_enabled" = "1" ]; then
|
|
||||||
has_server_sections=1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
config_foreach _check_server_sections server
|
|
||||||
|
|
||||||
# Add inline server ONLY if no separate server sections exist
|
# Add inline server ONLY if no separate server sections exist
|
||||||
# This prevents duplicate server names
|
# This prevents duplicate server names
|
||||||
@ -1059,11 +1098,20 @@ _generate_backend() {
|
|||||||
[ -n "$server_line" ] && echo " server $server_line"
|
[ -n "$server_line" ] && echo " server $server_line"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Add servers from separate server UCI sections
|
# Add servers from pre-collected data (avoiding nested config_foreach)
|
||||||
config_foreach _add_server_to_backend server "$name"
|
if [ -f "$_servers_data" ]; then
|
||||||
|
grep "^${name}|" "$_servers_data" 2>/dev/null | while IFS='|' read -r _backend srv_name srv_addr srv_port srv_weight srv_check; do
|
||||||
|
local check_opt=""
|
||||||
|
[ "$srv_check" = "1" ] && check_opt="check"
|
||||||
|
echo " server $srv_name $srv_addr:$srv_port weight $srv_weight $check_opt"
|
||||||
|
done
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
_add_server_to_backend() {
|
# DEPRECATED: This function is no longer used - server data is now pre-collected
|
||||||
|
# in _generate_backends() to avoid nested config_foreach bugs.
|
||||||
|
# Kept for reference only.
|
||||||
|
_add_server_to_backend_DEPRECATED() {
|
||||||
local section="$1"
|
local section="$1"
|
||||||
local target_backend="$2"
|
local target_backend="$2"
|
||||||
local backend server_name address port weight check enabled
|
local backend server_name address port weight check enabled
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user