feat: dynamic HAProxy binary path from cluster configuration

- /api/agents/{name}/config now returns haproxy_bin_path, haproxy_config_path, stats_socket_path from cluster
- Agent daemon uses these dynamic paths from API response for validation
- Fallback to local config file if API values not present
- Allows cluster admin to change paths without reinstalling agents

This fixes validation when HAProxy binary is in non-standard location
This commit is contained in:
taylanbakircioglu
2026-01-26 15:25:15 +03:00
parent 97e32114e0
commit e75bfdf706
3 changed files with 63 additions and 10 deletions
+9 -2
View File
@@ -1720,9 +1720,12 @@ async def get_agent_config(agent_name: str, x_api_key: Optional[str] = Header(No
conn = await get_database_connection()
# Get agent info first to check pool
# CRITICAL: Include cluster's haproxy_bin_path, haproxy_config_path, stats_socket_path
# These are needed for dynamic validation - cluster admin can change paths without reinstalling agent
agent_info = await conn.fetchrow("""
SELECT a.id, a.name, a.pool_id, hc.id as cluster_id, hc.name as cluster_name,
COALESCE(a.enabled, TRUE) as enabled
COALESCE(a.enabled, TRUE) as enabled,
hc.haproxy_bin_path, hc.haproxy_config_path, hc.stats_socket_path
FROM agents a
LEFT JOIN haproxy_clusters hc ON hc.pool_id = a.pool_id
WHERE a.name = $1
@@ -1817,7 +1820,11 @@ async def get_agent_config(agent_name: str, x_api_key: Optional[str] = Header(No
"config_content": config_version['config_content'],
"version": config_version['version_name'],
"checksum": config_version['checksum'],
"status": "available"
"status": "available",
# Dynamic paths from cluster configuration - agent should use these for validation
"haproxy_bin_path": agent_info.get('haproxy_bin_path', '/usr/sbin/haproxy'),
"haproxy_config_path": agent_info.get('haproxy_config_path', '/etc/haproxy/haproxy.cfg'),
"stats_socket_path": agent_info.get('stats_socket_path', '/var/run/haproxy/admin.sock')
}
except HTTPException:
+27 -4
View File
@@ -2855,10 +2855,33 @@ CONFIG_RESPONSE_EOF
echo "$daemon_config_content" > /tmp/haproxy-new-config.cfg 2>/dev/null
if [[ -f "/tmp/haproxy-new-config.cfg" && -s "/tmp/haproxy-new-config.cfg" ]]; then
# Get paths from config
HAPROXY_BIN=$(jq -r '.haproxy.bin_path' "$CONFIG_FILE" 2>/dev/null || echo "{{HAPROXY_BIN_PATH}}")
HAPROXY_CONFIG=$(jq -r '.haproxy.config_path' "$CONFIG_FILE" 2>/dev/null || echo "{{HAPROXY_CONFIG_PATH}}")
STATS_SOCKET=$(jq -r '.haproxy.stats_socket_path' "$CONFIG_FILE" 2>/dev/null || echo "{{STATS_SOCKET_PATH}}")
# Get paths DYNAMICALLY from API response first (cluster config can be changed without reinstall)
# Fallback to local config file if not present in API response
api_bin_path=$(echo "$config_response" | jq -r '.haproxy_bin_path // empty' 2>/dev/null)
api_config_path=$(echo "$config_response" | jq -r '.haproxy_config_path // empty' 2>/dev/null)
api_socket_path=$(echo "$config_response" | jq -r '.stats_socket_path // empty' 2>/dev/null)
# Use API values if present, otherwise fallback to local config
if [[ -n "$api_bin_path" && "$api_bin_path" != "null" ]]; then
HAPROXY_BIN="$api_bin_path"
log "DEBUG" "DAEMON: Using dynamic haproxy_bin_path from cluster: $HAPROXY_BIN"
else
HAPROXY_BIN=$(jq -r '.haproxy.bin_path' "$CONFIG_FILE" 2>/dev/null || echo "{{HAPROXY_BIN_PATH}}")
fi
if [[ -n "$api_config_path" && "$api_config_path" != "null" ]]; then
HAPROXY_CONFIG="$api_config_path"
log "DEBUG" "DAEMON: Using dynamic haproxy_config_path from cluster: $HAPROXY_CONFIG"
else
HAPROXY_CONFIG=$(jq -r '.haproxy.config_path' "$CONFIG_FILE" 2>/dev/null || echo "{{HAPROXY_CONFIG_PATH}}")
fi
if [[ -n "$api_socket_path" && "$api_socket_path" != "null" ]]; then
STATS_SOCKET="$api_socket_path"
log "DEBUG" "DAEMON: Using dynamic stats_socket_path from cluster: $STATS_SOCKET"
else
STATS_SOCKET=$(jq -r '.haproxy.stats_socket_path' "$CONFIG_FILE" 2>/dev/null || echo "{{STATS_SOCKET_PATH}}")
fi
# CRITICAL NEW FEATURE: Check if this is a partial config (only frontends/backends)
# Backend now generates partial configs to preserve global/defaults sections
+27 -4
View File
@@ -2803,10 +2803,33 @@ CONFIG_RESPONSE_EOF
echo "$daemon_config_content" > /tmp/haproxy-new-config.cfg 2>/dev/null
if [[ -f "/tmp/haproxy-new-config.cfg" && -s "/tmp/haproxy-new-config.cfg" ]]; then
# Get paths from config
HAPROXY_BIN=$(jq -r '.haproxy.bin_path' "$CONFIG_FILE" 2>/dev/null || echo "{{HAPROXY_BIN_PATH}}")
HAPROXY_CONFIG=$(jq -r '.haproxy.config_path' "$CONFIG_FILE" 2>/dev/null || echo "{{HAPROXY_CONFIG_PATH}}")
STATS_SOCKET=$(jq -r '.haproxy.stats_socket_path' "$CONFIG_FILE" 2>/dev/null || echo "{{STATS_SOCKET_PATH}}")
# Get paths DYNAMICALLY from API response first (cluster config can be changed without reinstall)
# Fallback to local config file if not present in API response
api_bin_path=$(echo "$config_response" | jq -r '.haproxy_bin_path // empty' 2>/dev/null)
api_config_path=$(echo "$config_response" | jq -r '.haproxy_config_path // empty' 2>/dev/null)
api_socket_path=$(echo "$config_response" | jq -r '.stats_socket_path // empty' 2>/dev/null)
# Use API values if present, otherwise fallback to local config
if [[ -n "$api_bin_path" && "$api_bin_path" != "null" ]]; then
HAPROXY_BIN="$api_bin_path"
log "DEBUG" "DAEMON: Using dynamic haproxy_bin_path from cluster: $HAPROXY_BIN"
else
HAPROXY_BIN=$(jq -r '.haproxy.bin_path' "$CONFIG_FILE" 2>/dev/null || echo "{{HAPROXY_BIN_PATH}}")
fi
if [[ -n "$api_config_path" && "$api_config_path" != "null" ]]; then
HAPROXY_CONFIG="$api_config_path"
log "DEBUG" "DAEMON: Using dynamic haproxy_config_path from cluster: $HAPROXY_CONFIG"
else
HAPROXY_CONFIG=$(jq -r '.haproxy.config_path' "$CONFIG_FILE" 2>/dev/null || echo "{{HAPROXY_CONFIG_PATH}}")
fi
if [[ -n "$api_socket_path" && "$api_socket_path" != "null" ]]; then
STATS_SOCKET="$api_socket_path"
log "DEBUG" "DAEMON: Using dynamic stats_socket_path from cluster: $STATS_SOCKET"
else
STATS_SOCKET=$(jq -r '.haproxy.stats_socket_path' "$CONFIG_FILE" 2>/dev/null || echo "{{STATS_SOCKET_PATH}}")
fi
# CRITICAL NEW FEATURE: Check if this is a partial config (only frontends/backends)
# Backend now generates partial configs to preserve global/defaults sections