From 8418025744adf1663199a796c2aa1e3ae770c32d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Taylan=20Bak=C4=B1rc=C4=B1o=C4=9Flu?= Date: Tue, 18 Nov 2025 19:11:20 +0300 Subject: [PATCH] fix(agent): auto-register agents with pool_id from cluster_id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PROBLEM: - Agents registered with pool_id = NULL - UI doesn't show agents without pool assignment - Auto-healing only works when server_statuses present - New agents with empty HAProxy config never get pool_id ROOT CAUSE: - Agent auto-register INSERT excluded pool_id column - Auto-healing (line 1417-1422) only triggers inside 'if server_statuses:' block - Fresh agents send no server_statuses → auto-healing never runs - Agent sits with pool_id = NULL indefinitely ANALYSIS: - Previous commit 1f9d8c2 added auto-healing for edge case (cluster before pool) - But auto-healing has conditional prerequisite: server_statuses must exist - New agents: empty HAProxy config → no server_statuses → no healing - This was working before because agents had config from start SOLUTION: - Extract pool_id from cluster_id during registration - Query haproxy_clusters for pool_id using heartbeat's cluster_id - Insert agent with pool_id immediately (no wait for auto-heal) - Two auto-register paths fixed: with API key + without API key EDGE CASES HANDLED: 1. cluster_id missing → pool_id = NULL → auto-healing fallback ✅ 2. cluster deleted → pool_id = NULL → auto-healing fallback ✅ 3. cluster pool_id NULL → pool_id = NULL → auto-healing works ✅ 4. Old agent script → no cluster_id → auto-healing works ✅ COMPATIBILITY: - Auto-healing preserved (line 1417-1422 untouched) - Two mechanisms work together harmoniously - Backward compatible with existing agents - No breaking changes IMPACT: - New agents visible in UI immediately - Faster registration (no wait for auto-heal) - Reduced heartbeat cycles to full functionality - Better user experience TESTING REQUIRED: 1. Delete agent from database 2. Install fresh agent with cluster_id in config 3. Verify pool_id populated on registration 4. Verify agent appears in UI immediately 5. Verify existing agents unaffected --- backend/routers/agent.py | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/backend/routers/agent.py b/backend/routers/agent.py index 2abfa04..fdc0e55 100644 --- a/backend/routers/agent.py +++ b/backend/routers/agent.py @@ -1244,12 +1244,22 @@ async def agent_heartbeat_by_name( # Auto-register new agent logger.info(f"Agent '{agent_name}' not found. Auto-registering...") try: + # Get pool_id from cluster_id if provided in heartbeat + pool_id_from_cluster = None + if heartbeat_dict.get('cluster_id'): + cluster_pool = await conn.fetchrow(""" + SELECT pool_id FROM haproxy_clusters WHERE id = $1 + """, heartbeat_dict['cluster_id']) + if cluster_pool: + pool_id_from_cluster = cluster_pool['pool_id'] + logger.info(f"Auto-register: Got pool_id {pool_id_from_cluster} from cluster_id {heartbeat_dict['cluster_id']}") + agent_id = await conn.fetchval(""" - INSERT INTO agents (name, status, last_seen, api_key) - VALUES ($1, 'online', CURRENT_TIMESTAMP, $2) RETURNING id - """, agent_name, x_api_key) - logger.info(f"Auto-registered new agent '{agent_name}' with ID {agent_id}") - agent = {'id': agent_id, 'pool_id': None} + INSERT INTO agents (name, status, last_seen, api_key, pool_id) + VALUES ($1, 'online', CURRENT_TIMESTAMP, $2, $3) RETURNING id + """, agent_name, x_api_key, pool_id_from_cluster) + logger.info(f"Auto-registered new agent '{agent_name}' with ID {agent_id} and pool_id {pool_id_from_cluster}") + agent = {'id': agent_id, 'pool_id': pool_id_from_cluster} except Exception as e: await close_database_connection(conn) logger.error(f"Failed to auto-register agent '{agent_name}': {e}", exc_info=True) @@ -1258,12 +1268,22 @@ async def agent_heartbeat_by_name( # Auto-register new agent without API key logger.info(f"Agent '{agent_name}' not found. Auto-registering...") try: + # Get pool_id from cluster_id if provided in heartbeat + pool_id_from_cluster = None + if heartbeat_dict.get('cluster_id'): + cluster_pool = await conn.fetchrow(""" + SELECT pool_id FROM haproxy_clusters WHERE id = $1 + """, heartbeat_dict['cluster_id']) + if cluster_pool: + pool_id_from_cluster = cluster_pool['pool_id'] + logger.info(f"Auto-register: Got pool_id {pool_id_from_cluster} from cluster_id {heartbeat_dict['cluster_id']}") + agent_id = await conn.fetchval(""" - INSERT INTO agents (name, status, last_seen) - VALUES ($1, 'online', CURRENT_TIMESTAMP) RETURNING id - """, agent_name) - logger.info(f"Auto-registered new agent '{agent_name}' with ID {agent_id}") - agent = {'id': agent_id, 'pool_id': None} + INSERT INTO agents (name, status, last_seen, pool_id) + VALUES ($1, 'online', CURRENT_TIMESTAMP, $2) RETURNING id + """, agent_name, pool_id_from_cluster) + logger.info(f"Auto-registered new agent '{agent_name}' with ID {agent_id} and pool_id {pool_id_from_cluster}") + agent = {'id': agent_id, 'pool_id': pool_id_from_cluster} except Exception as e: await close_database_connection(conn) logger.error(f"Failed to auto-register agent '{agent_name}': {e}", exc_info=True)