Add multi-address fallback for node connectivity

NodeConfig and MasterNodeConfig gain an optional addresses[] field
for fallback addresses tried in order after the primary address.
Provides resilience when Tailscale DNS is down or a node is only
reachable via LAN.

- dialAgentMulti: tries each address with a 3s health check, returns
  first success
- forEachNode: uses multi-address dialing
- AgentPool.AddNodeMulti: master tries all addresses when connecting
- AllAddresses(): deduplicates primary + fallback addresses

Config example:
  [[nodes]]
  name = "rift"
  address = "rift.scylla-hammerhead.ts.net:9444"
  addresses = ["100.95.252.120:9444", "192.168.88.181:9444"]

Existing configs without addresses[] work unchanged.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-03 09:45:50 -07:00
parent 5da307cab5
commit f9f6f339f4
7 changed files with 114 additions and 22 deletions

View File

@@ -140,21 +140,30 @@ func NewAgentPool(caCertPath, token string) *AgentPool {
// AddNode dials an agent and adds it to the pool.
func (p *AgentPool) AddNode(name, address string) error {
client, err := DialAgent(address, p.caCert, p.token)
if err != nil {
return fmt.Errorf("add node %s: %w", name, err)
}
client.Node = name
return p.AddNodeMulti(name, []string{address})
}
p.mu.Lock()
defer p.mu.Unlock()
// AddNodeMulti tries each address in order and adds the first successful
// connection to the pool.
func (p *AgentPool) AddNodeMulti(name string, addresses []string) error {
var lastErr error
for _, addr := range addresses {
client, err := DialAgent(addr, p.caCert, p.token)
if err != nil {
lastErr = fmt.Errorf("%s: %w", addr, err)
continue
}
client.Node = name
// Close existing connection if re-adding.
if old, ok := p.clients[name]; ok {
_ = old.Close()
p.mu.Lock()
if old, ok := p.clients[name]; ok {
_ = old.Close()
}
p.clients[name] = client
p.mu.Unlock()
return nil
}
p.clients[name] = client
return nil
return fmt.Errorf("add node %s: all addresses failed: %w", name, lastErr)
}
// Get returns the agent client for a node.