github.imxd.top/hashicorp/consul@v1.4.5/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  }