github.com/sl1pm4t/consul@v1.4.5-0.20190325224627-74c31c540f9c/agent/router/serf_flooder.go (about) 1 package router 2 3 import ( 4 "fmt" 5 "log" 6 "net" 7 "strings" 8 9 "github.com/hashicorp/consul/agent/metadata" 10 "github.com/hashicorp/serf/serf" 11 ) 12 13 // FloodAddrFn gets the address to use for a given server when flood-joining. This 14 // will return false if it doesn't have one. 15 type FloodAddrFn func(*metadata.Server) (string, bool) 16 17 // FloodPortFn gets the port to use for a given server when flood-joining. This 18 // will return false if it doesn't have one. 19 type FloodPortFn func(*metadata.Server) (int, bool) 20 21 // FloodJoins attempts to make sure all Consul servers in the local Serf 22 // instance are joined in the global Serf instance. It assumes names in the 23 // local area are of the form <node> and those in the global area are of the 24 // form <node>.<dc> as is done for WAN and general network areas in Consul 25 // Enterprise. 26 func FloodJoins(logger *log.Logger, addrFn FloodAddrFn, portFn FloodPortFn, 27 localDatacenter string, localSerf *serf.Serf, globalSerf *serf.Serf) { 28 29 // Names in the global Serf have the datacenter suffixed. 30 suffix := fmt.Sprintf(".%s", localDatacenter) 31 32 // Index the global side so we can do one pass through the local side 33 // with cheap lookups. 34 index := make(map[string]*metadata.Server) 35 for _, m := range globalSerf.Members() { 36 ok, server := metadata.IsConsulServer(m) 37 if !ok { 38 continue 39 } 40 41 if server.Datacenter != localDatacenter { 42 continue 43 } 44 45 localName := strings.TrimSuffix(server.Name, suffix) 46 index[localName] = server 47 } 48 49 // Now run through the local side and look for joins. 50 for _, m := range localSerf.Members() { 51 if m.Status != serf.StatusAlive { 52 continue 53 } 54 55 ok, server := metadata.IsConsulServer(m) 56 if !ok { 57 continue 58 } 59 60 if _, ok := index[server.Name]; ok { 61 continue 62 } 63 64 // We can't use the port number from the local Serf, so we just 65 // get the host part. 66 addr, _, err := net.SplitHostPort(server.Addr.String()) 67 if err != nil { 68 logger.Printf("[DEBUG] consul: Failed to flood-join %q (bad address %q): %v", 69 server.Name, server.Addr.String(), err) 70 } 71 if addrFn != nil { 72 if a, ok := addrFn(server); ok { 73 addr = a 74 } 75 } 76 77 // Let the callback see if it can get the port number, otherwise 78 // leave it blank to behave as if we just supplied an address. 79 if port, ok := portFn(server); ok { 80 addr = net.JoinHostPort(addr, fmt.Sprintf("%d", port)) 81 } else { 82 // If we have an IPv6 address, we should add brackets, 83 // single globalSerf.Join expects that. 84 if ip := net.ParseIP(addr); ip != nil { 85 if ip.To4() == nil { 86 addr = fmt.Sprintf("[%s]", addr) 87 } 88 } else { 89 logger.Printf("[DEBUG] consul: Failed to parse IP %s", addr) 90 } 91 } 92 93 // Do the join! 94 n, err := globalSerf.Join([]string{addr}, true) 95 if err != nil { 96 logger.Printf("[DEBUG] consul: Failed to flood-join %q at %s: %v", 97 server.Name, addr, err) 98 } else if n > 0 { 99 logger.Printf("[DEBUG] consul: Successfully performed flood-join for %q at %s", 100 server.Name, addr) 101 } 102 } 103 }