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

@@ -62,9 +62,24 @@ type TimeoutsConfig struct {
// MasterNodeConfig is a bootstrap node entry in the master config.
type MasterNodeConfig struct {
Name string `toml:"name"`
Address string `toml:"address"`
Role string `toml:"role"` // "worker", "edge", or "master"
Name string `toml:"name"`
Address string `toml:"address"`
Addresses []string `toml:"addresses,omitempty"`
Role string `toml:"role"` // "worker", "edge", or "master"
}
// AllAddresses returns the node's primary address followed by any
// fallback addresses, deduplicated.
func (n MasterNodeConfig) AllAddresses() []string {
seen := make(map[string]bool)
var addrs []string
for _, a := range append([]string{n.Address}, n.Addresses...) {
if a != "" && !seen[a] {
seen[a] = true
addrs = append(addrs, a)
}
}
return addrs
}
// LoadMasterConfig reads and validates a master configuration file.