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
|
||||
|
||||
# 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
|
||||
_generated_backends=""
|
||||
|
||||
# Generate each backend from UCI
|
||||
# Generate each backend from UCI (now safe - no nested config_foreach needed)
|
||||
config_foreach _generate_backend backend
|
||||
|
||||
# Cleanup server data temp file
|
||||
rm -f "$_servers_data"
|
||||
|
||||
# Collect all backends referenced by vhosts
|
||||
_referenced_backends=""
|
||||
_collect_vhost_backend() {
|
||||
@ -1039,17 +1083,12 @@ _generate_backend() {
|
||||
fi
|
||||
|
||||
# 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
|
||||
_check_server_sections() {
|
||||
local srv_section="$1"
|
||||
local srv_backend
|
||||
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
|
||||
if [ -f "$_servers_data" ] && grep -q "^${name}|" "$_servers_data"; then
|
||||
has_server_sections=1
|
||||
fi
|
||||
|
||||
# Add inline server ONLY if no separate server sections exist
|
||||
# This prevents duplicate server names
|
||||
@ -1059,11 +1098,20 @@ _generate_backend() {
|
||||
[ -n "$server_line" ] && echo " server $server_line"
|
||||
fi
|
||||
|
||||
# Add servers from separate server UCI sections
|
||||
config_foreach _add_server_to_backend server "$name"
|
||||
# Add servers from pre-collected data (avoiding nested config_foreach)
|
||||
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 target_backend="$2"
|
||||
local backend server_name address port weight check enabled
|
||||
|
||||
Loading…
Reference in New Issue
Block a user