From bc2a45a9de23c9adaa9f2a6d450d2c6ef1740513 Mon Sep 17 00:00:00 2001 From: James Phillips Date: Wed, 8 Feb 2017 13:56:07 -0800 Subject: [PATCH] Updates memberlist and Serf (and adds new dependencies). This gets a number of improvements: * Fixed a missing case where gossip would stop flowing to dead nodes for a short while. * Uses the go-sockaddr library to look for private IP addresses, which prefers non-loopback private addresses over loopback ones when trying to automatically determine the advertise address. * Properly seeds Go's random number generator using the seed library. * Serf snapshots no longer have the executable bit set on thie file. --- .../github.com/hashicorp/go-sockaddr/Makefile | 63 +++++++++ .../hashicorp/go-sockaddr/README.md | 118 ++++++++++++++++ .../github.com/hashicorp/go-sockaddr/doc.go | 5 + .../hashicorp/go-sockaddr/ifaddr.go | 6 +- .../hashicorp/go-sockaddr/ifaddrs.go | 4 +- .../hashicorp/go-sockaddr/route_info_bsd.go | 2 +- .../hashicorp/go-sockaddr/route_info_linux.go | 2 +- .../go-sockaddr/route_info_solaris.go | 2 +- .../go-sockaddr/route_info_windows.go | 2 +- .../github.com/hashicorp/memberlist/config.go | 7 + .../hashicorp/memberlist/memberlist.go | 57 ++++---- vendor/github.com/hashicorp/memberlist/net.go | 3 +- .../github.com/hashicorp/memberlist/state.go | 24 ++-- .../github.com/hashicorp/memberlist/util.go | 127 ++---------------- .../github.com/hashicorp/serf/serf/config.go | 7 + vendor/github.com/hashicorp/serf/serf/serf.go | 17 ++- .../hashicorp/serf/serf/snapshot.go | 2 +- vendor/github.com/sean-/seed/LICENSE | 22 +++ vendor/github.com/sean-/seed/README.md | 44 ++++++ vendor/github.com/sean-/seed/init.go | 65 +++++++++ vendor/vendor.json | 24 ++-- 21 files changed, 422 insertions(+), 181 deletions(-) create mode 100644 vendor/github.com/hashicorp/go-sockaddr/Makefile create mode 100644 vendor/github.com/hashicorp/go-sockaddr/README.md create mode 100644 vendor/github.com/hashicorp/go-sockaddr/doc.go create mode 100644 vendor/github.com/sean-/seed/LICENSE create mode 100644 vendor/github.com/sean-/seed/README.md create mode 100644 vendor/github.com/sean-/seed/init.go diff --git a/vendor/github.com/hashicorp/go-sockaddr/Makefile b/vendor/github.com/hashicorp/go-sockaddr/Makefile new file mode 100644 index 000000000000..224135dc1e7d --- /dev/null +++ b/vendor/github.com/hashicorp/go-sockaddr/Makefile @@ -0,0 +1,63 @@ +TOOLS= golang.org/x/tools/cover +GOCOVER_TMPFILE?= $(GOCOVER_FILE).tmp +GOCOVER_FILE?= .cover.out +GOCOVERHTML?= coverage.html + +test:: $(GOCOVER_FILE) + @$(MAKE) -C cmd/sockaddr test + +cover:: coverage_report + +$(GOCOVER_FILE):: + @find . -type d ! -path '*cmd*' ! -path '*.git*' -print0 | xargs -0 -I % sh -ec "cd % && rm -f $(GOCOVER_TMPFILE) && go test -coverprofile=$(GOCOVER_TMPFILE)" + + @echo 'mode: set' > $(GOCOVER_FILE) + @find . -type f ! -path '*cmd*' ! -path '*.git*' -name "$(GOCOVER_TMPFILE)" -print0 | xargs -0 -n1 cat $(GOCOVER_TMPFILE) | grep -v '^mode: ' >> ${PWD}/$(GOCOVER_FILE) + +$(GOCOVERHTML): $(GOCOVER_FILE) + go tool cover -html=$(GOCOVER_FILE) -o $(GOCOVERHTML) + +coverage_report:: $(GOCOVER_FILE) + go tool cover -html=$(GOCOVER_FILE) + +audit_tools:: + @go get -u github.com/golang/lint/golint && echo "Installed golint:" + @go get -u github.com/fzipp/gocyclo && echo "Installed gocyclo:" + @go get -u github.com/remyoudompheng/go-misc/deadcode && echo "Installed deadcode:" + @go get -u github.com/client9/misspell/cmd/misspell && echo "Installed misspell:" + @go get -u github.com/gordonklaus/ineffassign && echo "Installed ineffassign:" + +audit:: + deadcode + go tool vet -all *.go + go tool vet -shadow=true *.go + golint *.go + ineffassign . + gocyclo -over 65 *.go + misspell *.go + +clean:: + rm -f $(GOCOVER_FILE) $(GOCOVERHTML) + +dev:: + @go build + @make -B -C cmd/sockaddr sockaddr + +install:: + @go install + @make -C cmd/sockaddr install + +doc:: + echo Visit: http://127.0.0.1:6060/pkg/github.com/hashicorp/go-sockaddr/ + godoc -http=:6060 -goroot $GOROOT + +world:: + @set -e; \ + for os in solaris darwin freebsd linux windows; do \ + for arch in amd64; do \ + printf "Building on %s-%s\n" "$${os}" "$${arch}" ; \ + env GOOS="$${os}" GOARCH="$${arch}" go build -o /dev/null; \ + done; \ + done + + make -C cmd/sockaddr world diff --git a/vendor/github.com/hashicorp/go-sockaddr/README.md b/vendor/github.com/hashicorp/go-sockaddr/README.md new file mode 100644 index 000000000000..5273ee8998a8 --- /dev/null +++ b/vendor/github.com/hashicorp/go-sockaddr/README.md @@ -0,0 +1,118 @@ +# go-sockaddr + +## `sockaddr` Library + +Socket address convenience functions for Go. `go-sockaddr` is a convenience +library that makes doing the right thing with IP addresses easy. `go-sockaddr` +is loosely modeled after the UNIX `sockaddr_t` and creates a union of the family +of `sockaddr_t` types (see below for an ascii diagram). Library documentation +is available +at +[https://godoc.org/github.com/hashicorp/go-sockaddr](https://godoc.org/github.com/hashicorp/go-sockaddr). +The primary intent of the library was to make it possible to define heuristics +for selecting the correct IP addresses when a configuration is evaluated at +runtime. See +the +[docs](https://godoc.org/github.com/hashicorp/go-sockaddr), +[`template` package](https://godoc.org/github.com/hashicorp/go-sockaddr/template), +tests, +and +[CLI utility](https://github.com/hashicorp/go-sockaddr/tree/master/cmd/sockaddr) +for details and hints as to how to use this library. + +For example, with this library it is possible to find an IP address that: + +* is attached to a default route + ([`GetDefaultInterfaces()`](https://godoc.org/github.com/hashicorp/go-sockaddr#GetDefaultInterfaces)) +* is contained within a CIDR block (['IfByNetwork()'](https://godoc.org/github.com/hashicorp/go-sockaddr#IfByNetwork)) +* is an RFC1918 address + ([`IfByRFC("1918")`](https://godoc.org/github.com/hashicorp/go-sockaddr#IfByRFC)) +* is ordered + ([`OrderedIfAddrBy(args)`](https://godoc.org/github.com/hashicorp/go-sockaddr#OrderedIfAddrBy) where + `args` includes, but is not limited + to, + [`AscIfType`](https://godoc.org/github.com/hashicorp/go-sockaddr#AscIfType), + [`AscNetworkSize`](https://godoc.org/github.com/hashicorp/go-sockaddr#AscNetworkSize)) +* excludes all IPv6 addresses + ([`IfByType("^(IPv4)$")`](https://godoc.org/github.com/hashicorp/go-sockaddr#IfByType)) +* is larger than a `/32` + ([`IfByMaskSize(32)`](https://godoc.org/github.com/hashicorp/go-sockaddr#IfByMaskSize)) +* is not on a `down` interface + ([`ExcludeIfs("flags", "down")`](https://godoc.org/github.com/hashicorp/go-sockaddr#ExcludeIfs)) +* preferences an IPv6 address over an IPv4 address + ([`SortIfByType()`](https://godoc.org/github.com/hashicorp/go-sockaddr#SortIfByType) + + [`ReverseIfAddrs()`](https://godoc.org/github.com/hashicorp/go-sockaddr#ReverseIfAddrs)); and +* excludes any IP in RFC6890 address + ([`IfByRFC("6890")`](https://godoc.org/github.com/hashicorp/go-sockaddr#IfByRFC)) + +Or any combination or variation therein. + +There are also a few simple helper functions such as `GetPublicIP` and +`GetPrivateIP` which both return strings and select the first public or private +IP address on the default interface, respectively. Similarly, there is also a +helper function called `GetInterfaceIP` which returns the first usable IP +address on the named interface. + +## `sockaddr` CLI + +Given the possible complexity of the `sockaddr` library, there is a CLI utility +that accompanies the library, also +called +[`sockaddr`](https://github.com/hashicorp/go-sockaddr/tree/master/cmd/sockaddr). +The +[`sockaddr`](https://github.com/hashicorp/go-sockaddr/tree/master/cmd/sockaddr) +utility exposes nearly all of the functionality of the library and can be used +either as an administrative tool or testing tool. To install +the +[`sockaddr`](https://github.com/hashicorp/go-sockaddr/tree/master/cmd/sockaddr), +run: + +```text +$ go get -u github.com/hashicorp/go-sockaddr/cmd/sockaddr +``` + +If you're familiar with UNIX's `sockaddr` struct's, the following diagram +mapping the C `sockaddr` (top) to `go-sockaddr` structs (bottom) and +interfaces will be helpful: + +``` ++-------------------------------------------------------+ +| | +| sockaddr | +| SockAddr | +| | +| +--------------+ +----------------------------------+ | +| | sockaddr_un | | | | +| | SockAddrUnix | | sockaddr_in{,6} | | +| +--------------+ | IPAddr | | +| | | | +| | +-------------+ +--------------+ | | +| | | sockaddr_in | | sockaddr_in6 | | | +| | | IPv4Addr | | IPv6Addr | | | +| | +-------------+ +--------------+ | | +| | | | +| +----------------------------------+ | +| | ++-------------------------------------------------------+ +``` + +## Inspiration and Design + +There were many subtle inspirations that led to this design, but the most direct +inspiration for the filtering syntax was +OpenBSD's +[`pf.conf(5)`](https://www.freebsd.org/cgi/man.cgi?query=pf.conf&apropos=0&sektion=0&arch=default&format=html#PARAMETERS) firewall +syntax that lets you select the first IP address on a given named interface. +The original problem stemmed from: + +* needing to create immutable images using [Packer](https://www.packer.io) that + ran the [Consul](https://www.consul.io) process (Consul can only use one IP + address at a time); +* images that may or may not have multiple interfaces or IP addresses at + runtime; and +* we didn't want to rely on configuration management to render out the correct + IP address if the VM image was being used in an auto-scaling group. + +Instead we needed some way to codify a heuristic that would correctly select the +right IP address but the input parameters were not known when the image was +created. diff --git a/vendor/github.com/hashicorp/go-sockaddr/doc.go b/vendor/github.com/hashicorp/go-sockaddr/doc.go new file mode 100644 index 000000000000..90671deb51d8 --- /dev/null +++ b/vendor/github.com/hashicorp/go-sockaddr/doc.go @@ -0,0 +1,5 @@ +/* +Package sockaddr is a Go implementation of the UNIX socket family data types and +related helper functions. +*/ +package sockaddr diff --git a/vendor/github.com/hashicorp/go-sockaddr/ifaddr.go b/vendor/github.com/hashicorp/go-sockaddr/ifaddr.go index dc1ba72e7979..3e4ff9fca433 100644 --- a/vendor/github.com/hashicorp/go-sockaddr/ifaddr.go +++ b/vendor/github.com/hashicorp/go-sockaddr/ifaddr.go @@ -11,7 +11,7 @@ func init() { // GetPrivateIP returns a string with a single IP address that is part of RFC // 6890 and has a default route. If the system can't determine its IP address // or find an RFC 6890 IP address, an empty string will be returned instead. -// This function is the `eval` equivilant of: +// This function is the `eval` equivalent of: // // ``` // $ sockaddr eval -r '{{GetPrivateInterfaces | attr "address"}}' @@ -33,7 +33,7 @@ func GetPrivateIP() (string, error) { // GetPublicIP returns a string with a single IP address that is NOT part of RFC // 6890 and has a default route. If the system can't determine its IP address // or find a non RFC 6890 IP address, an empty string will be returned instead. -// This function is the `eval` equivilant of: +// This function is the `eval` equivalent of: // // ``` // $ sockaddr eval -r '{{GetPublicInterfaces | attr "address"}}' @@ -53,7 +53,7 @@ func GetPublicIP() (string, error) { // GetInterfaceIP returns a string with a single IP address sorted by the size // of the network (i.e. IP addresses with a smaller netmask, larger network -// size, are sorted first). This function is the `eval` equivilant of: +// size, are sorted first). This function is the `eval` equivalent of: // // ``` // $ sockaddr eval -r '{{GetAllInterfaces | include "name" <> | sort "type,size" | include "flag" "forwardable" | attr "address" }}' diff --git a/vendor/github.com/hashicorp/go-sockaddr/ifaddrs.go b/vendor/github.com/hashicorp/go-sockaddr/ifaddrs.go index 119360898665..8233be2022bf 100644 --- a/vendor/github.com/hashicorp/go-sockaddr/ifaddrs.go +++ b/vendor/github.com/hashicorp/go-sockaddr/ifaddrs.go @@ -240,7 +240,7 @@ func GetDefaultInterfaces() (IfAddrs, error) { // GetPrivateInterfaces returns an IfAddrs that are part of RFC 6890 and have a // default route. If the system can't determine its IP address or find an RFC // 6890 IP address, an empty IfAddrs will be returned instead. This function is -// the `eval` equivilant of: +// the `eval` equivalent of: // // ``` // $ sockaddr eval -r '{{GetDefaultInterfaces | include "type" "ip" | include "flags" "forwardable|up" | sort "type,size" | include "RFC" "6890" }}' @@ -282,7 +282,7 @@ func GetPrivateInterfaces() (IfAddrs, error) { // GetPublicInterfaces returns an IfAddrs that are NOT part of RFC 6890 and has a // default route. If the system can't determine its IP address or find a non // RFC 6890 IP address, an empty IfAddrs will be returned instead. This -// function is the `eval` equivilant of: +// function is the `eval` equivalent of: // // ``` // $ sockaddr eval -r '{{GetDefaultInterfaces | include "type" "ip" | include "flags" "forwardable|up" | sort "type,size" | exclude "RFC" "6890" }}' diff --git a/vendor/github.com/hashicorp/go-sockaddr/route_info_bsd.go b/vendor/github.com/hashicorp/go-sockaddr/route_info_bsd.go index 4b378561559b..705757abc7bb 100644 --- a/vendor/github.com/hashicorp/go-sockaddr/route_info_bsd.go +++ b/vendor/github.com/hashicorp/go-sockaddr/route_info_bsd.go @@ -20,7 +20,7 @@ func NewRouteInfo() (routeInfo, error) { }, nil } -// GetDefaultInterfaceName returns the interace name attached to the default +// GetDefaultInterfaceName returns the interface name attached to the default // route on the default interface. func (ri routeInfo) GetDefaultInterfaceName() (string, error) { out, err := exec.Command(cmds["route"][0], cmds["route"][1:]...).Output() diff --git a/vendor/github.com/hashicorp/go-sockaddr/route_info_linux.go b/vendor/github.com/hashicorp/go-sockaddr/route_info_linux.go index ace8feb0faa5..b33e4c0d0848 100644 --- a/vendor/github.com/hashicorp/go-sockaddr/route_info_linux.go +++ b/vendor/github.com/hashicorp/go-sockaddr/route_info_linux.go @@ -21,7 +21,7 @@ func NewRouteInfo() (routeInfo, error) { }, nil } -// GetDefaultInterfaceName returns the interace name attached to the default +// GetDefaultInterfaceName returns the interface name attached to the default // route on the default interface. func (ri routeInfo) GetDefaultInterfaceName() (string, error) { out, err := exec.Command(cmds["ip"][0], cmds["ip"][1:]...).Output() diff --git a/vendor/github.com/hashicorp/go-sockaddr/route_info_solaris.go b/vendor/github.com/hashicorp/go-sockaddr/route_info_solaris.go index 65e95fd97dad..ee8e7984d792 100644 --- a/vendor/github.com/hashicorp/go-sockaddr/route_info_solaris.go +++ b/vendor/github.com/hashicorp/go-sockaddr/route_info_solaris.go @@ -21,7 +21,7 @@ func NewRouteInfo() (routeInfo, error) { }, nil } -// GetDefaultInterfaceName returns the interace name attached to the default +// GetDefaultInterfaceName returns the interface name attached to the default // route on the default interface. func (ri routeInfo) GetDefaultInterfaceName() (string, error) { out, err := exec.Command(cmds["route"][0], cmds["route"][1:]...).Output() diff --git a/vendor/github.com/hashicorp/go-sockaddr/route_info_windows.go b/vendor/github.com/hashicorp/go-sockaddr/route_info_windows.go index fc4596d9c05d..3da972883e81 100644 --- a/vendor/github.com/hashicorp/go-sockaddr/route_info_windows.go +++ b/vendor/github.com/hashicorp/go-sockaddr/route_info_windows.go @@ -19,7 +19,7 @@ func NewRouteInfo() (routeInfo, error) { }, nil } -// GetDefaultInterfaceName returns the interace name attached to the default +// GetDefaultInterfaceName returns the interface name attached to the default // route on the default interface. func (ri routeInfo) GetDefaultInterfaceName() (string, error) { ifNameOut, err := exec.Command(cmds["netstat"][0], cmds["netstat"][1:]...).Output() diff --git a/vendor/github.com/hashicorp/memberlist/config.go b/vendor/github.com/hashicorp/memberlist/config.go index 498a62c548d9..1c13bfcd3666 100644 --- a/vendor/github.com/hashicorp/memberlist/config.go +++ b/vendor/github.com/hashicorp/memberlist/config.go @@ -188,6 +188,12 @@ type Config struct { // size of this determines the size of the queue which Memberlist will keep // while UDP messages are handled. HandoffQueueDepth int + + // Maximum number of bytes that memberlist expects UDP messages to be. A safe + // value for this is typically 1400 bytes (which is the default.) However, + // depending on your network's MTU (Maximum Transmission Unit) you may be able + // to increase this. + UDPBufferSize int } // DefaultLANConfig returns a sane set of configurations for Memberlist. @@ -228,6 +234,7 @@ func DefaultLANConfig() *Config { DNSConfigPath: "/etc/resolv.conf", HandoffQueueDepth: 1024, + UDPBufferSize: 1400, } } diff --git a/vendor/github.com/hashicorp/memberlist/memberlist.go b/vendor/github.com/hashicorp/memberlist/memberlist.go index 7e696762599d..371e3294b0c6 100644 --- a/vendor/github.com/hashicorp/memberlist/memberlist.go +++ b/vendor/github.com/hashicorp/memberlist/memberlist.go @@ -25,6 +25,7 @@ import ( "time" "github.com/hashicorp/go-multierror" + sockaddr "github.com/hashicorp/go-sockaddr" "github.com/miekg/dns" ) @@ -326,7 +327,7 @@ func (m *Memberlist) resolveAddr(hostStr string) ([]ipPort, error) { // as if we received an alive notification our own network channel for // ourself. func (m *Memberlist) setAlive() error { - var advertiseAddr []byte + var advertiseAddr net.IP var advertisePort int if m.config.AdvertiseAddr != "" { // If AdvertiseAddr is not empty, then advertise @@ -345,42 +346,21 @@ func (m *Memberlist) setAlive() error { advertisePort = m.config.AdvertisePort } else { if m.config.BindAddr == "0.0.0.0" { - // Otherwise, if we're not bound to a specific IP, - //let's list the interfaces on this machine and use - // the first private IP we find. - addresses, err := net.InterfaceAddrs() + // Otherwise, if we're not bound to a specific IP, let's use a suitable + // private IP address. + var err error + m.config.AdvertiseAddr, err = sockaddr.GetPrivateIP() if err != nil { - return fmt.Errorf("Failed to get interface addresses! Err: %v", err) + return fmt.Errorf("Failed to get interface addresses: %v", err) } - - // Find private IPv4 address - for _, rawAddr := range addresses { - var ip net.IP - switch addr := rawAddr.(type) { - case *net.IPAddr: - ip = addr.IP - case *net.IPNet: - ip = addr.IP - default: - continue - } - - if ip.To4() == nil { - continue - } - if !IsPrivateIP(ip.String()) { - continue - } - - advertiseAddr = ip - break + if m.config.AdvertiseAddr == "" { + return fmt.Errorf("No private IP address found, and explicit IP not provided") } - // Failed to find private IP, error + advertiseAddr = net.ParseIP(m.config.AdvertiseAddr) if advertiseAddr == nil { - return fmt.Errorf("No private IP address found, and explicit IP not provided") + return fmt.Errorf("Failed to parse advertise address: %q", m.config.AdvertiseAddr) } - } else { // Use the IP that we're bound to. addr := m.tcpListener.Addr().(*net.TCPAddr) @@ -392,8 +372,19 @@ func (m *Memberlist) setAlive() error { } // Check if this is a public address without encryption - addrStr := net.IP(advertiseAddr).String() - if !IsPrivateIP(addrStr) && !isLoopbackIP(addrStr) && !m.config.EncryptionEnabled() { + ipAddr, err := sockaddr.NewIPAddr(advertiseAddr.String()) + if err != nil { + return fmt.Errorf("Failed to parse interface addresses: %v", err) + } + + ifAddrs := []sockaddr.IfAddr{ + sockaddr.IfAddr{ + SockAddr: ipAddr, + }, + } + + _, publicIfs, err := sockaddr.IfByRFC("6890", ifAddrs) + if len(publicIfs) > 0 && !m.config.EncryptionEnabled() { m.logger.Printf("[WARN] memberlist: Binding to public address without encryption!") } diff --git a/vendor/github.com/hashicorp/memberlist/net.go b/vendor/github.com/hashicorp/memberlist/net.go index 378adba78c2d..e47da411ec32 100644 --- a/vendor/github.com/hashicorp/memberlist/net.go +++ b/vendor/github.com/hashicorp/memberlist/net.go @@ -70,7 +70,6 @@ const ( compoundOverhead = 2 // Assumed overhead per entry in compoundHeader udpBufSize = 65536 udpRecvBuf = 2 * 1024 * 1024 - udpSendBuf = 1400 userMsgOverhead = 1 blockingWarning = 10 * time.Millisecond // Warn if a UDP packet takes this long to process maxPushStateBytes = 10 * 1024 * 1024 @@ -605,7 +604,7 @@ func (m *Memberlist) encodeAndSendMsg(to net.Addr, msgType messageType, msg inte // create a compoundMsg and piggy back other broadcasts func (m *Memberlist) sendMsg(to net.Addr, msg []byte) error { // Check if we can piggy back any messages - bytesAvail := udpSendBuf - len(msg) - compoundHeaderOverhead + bytesAvail := m.config.UDPBufferSize - len(msg) - compoundHeaderOverhead if m.config.EncryptionEnabled() { bytesAvail -= encryptOverhead(m.encryptionVersion()) } diff --git a/vendor/github.com/hashicorp/memberlist/state.go b/vendor/github.com/hashicorp/memberlist/state.go index cc422bc1a3c4..6b9122f08c81 100644 --- a/vendor/github.com/hashicorp/memberlist/state.go +++ b/vendor/github.com/hashicorp/memberlist/state.go @@ -439,8 +439,8 @@ func (m *Memberlist) resetNodes() { m.nodeLock.Lock() defer m.nodeLock.Unlock() - // Move the dead nodes - deadIdx := moveDeadNodes(m.nodes) + // Move dead nodes, but respect gossip to the dead interval + deadIdx := moveDeadNodes(m.nodes, m.config.GossipToTheDeadTime) // Deregister the dead nodes for i := deadIdx; i < len(m.nodes); i++ { @@ -484,7 +484,7 @@ func (m *Memberlist) gossip() { m.nodeLock.RUnlock() // Compute the bytes available - bytesAvail := udpSendBuf - compoundHeaderOverhead + bytesAvail := m.config.UDPBufferSize - compoundHeaderOverhead if m.config.EncryptionEnabled() { bytesAvail -= encryptOverhead(m.encryptionVersion()) } @@ -496,13 +496,19 @@ func (m *Memberlist) gossip() { return } - // Create a compound message - compound := makeCompoundMessage(msgs) - - // Send the compound message destAddr := &net.UDPAddr{IP: node.Addr, Port: int(node.Port)} - if err := m.rawSendMsgUDP(destAddr, &node.Node, compound.Bytes()); err != nil { - m.logger.Printf("[ERR] memberlist: Failed to send gossip to %s: %s", destAddr, err) + + if len(msgs) == 1 { + // Send single message as is + if err := m.rawSendMsgUDP(destAddr, &node.Node, msgs[0]); err != nil { + m.logger.Printf("[ERR] memberlist: Failed to send gossip to %s: %s", destAddr, err) + } + } else { + // Otherwise create and send a compound message + compound := makeCompoundMessage(msgs) + if err := m.rawSendMsgUDP(destAddr, &node.Node, compound.Bytes()); err != nil { + m.logger.Printf("[ERR] memberlist: Failed to send gossip to %s: %s", destAddr, err) + } } } } diff --git a/vendor/github.com/hashicorp/memberlist/util.go b/vendor/github.com/hashicorp/memberlist/util.go index a294c3034d71..2ee58ba10bac 100644 --- a/vendor/github.com/hashicorp/memberlist/util.go +++ b/vendor/github.com/hashicorp/memberlist/util.go @@ -8,11 +8,11 @@ import ( "io" "math" "math/rand" - "net" "strings" "time" "github.com/hashicorp/go-msgpack/codec" + "github.com/sean-/seed" ) // pushPullScale is the minimum number of nodes @@ -22,72 +22,13 @@ import ( // while the 65th will triple it. const pushPullScaleThreshold = 32 -/* - * Contains an entry for each private block: - * 10.0.0.0/8 - * 100.64.0.0/10 - * 127.0.0.0/8 - * 169.254.0.0/16 - * 172.16.0.0/12 - * 192.168.0.0/16 - */ -var privateBlocks []*net.IPNet - -var loopbackBlock *net.IPNet - const ( // Constant litWidth 2-8 lzwLitWidth = 8 ) func init() { - // Seed the random number generator - rand.Seed(time.Now().UnixNano()) - - // Add each private block - privateBlocks = make([]*net.IPNet, 6) - - _, block, err := net.ParseCIDR("10.0.0.0/8") - if err != nil { - panic(fmt.Sprintf("Bad cidr. Got %v", err)) - } - privateBlocks[0] = block - - _, block, err = net.ParseCIDR("100.64.0.0/10") - if err != nil { - panic(fmt.Sprintf("Bad cidr. Got %v", err)) - } - privateBlocks[1] = block - - _, block, err = net.ParseCIDR("127.0.0.0/8") - if err != nil { - panic(fmt.Sprintf("Bad cidr. Got %v", err)) - } - privateBlocks[2] = block - - _, block, err = net.ParseCIDR("169.254.0.0/16") - if err != nil { - panic(fmt.Sprintf("Bad cidr. Got %v", err)) - } - privateBlocks[3] = block - - _, block, err = net.ParseCIDR("172.16.0.0/12") - if err != nil { - panic(fmt.Sprintf("Bad cidr. Got %v", err)) - } - privateBlocks[4] = block - - _, block, err = net.ParseCIDR("192.168.0.0/16") - if err != nil { - panic(fmt.Sprintf("Bad cidr. Got %v", err)) - } - privateBlocks[5] = block - - _, block, err = net.ParseCIDR("127.0.0.0/8") - if err != nil { - panic(fmt.Sprintf("Bad cidr. Got %v", err)) - } - loopbackBlock = block + seed.Init() } // Decode reverses the encode operation on a byte slice input @@ -108,42 +49,6 @@ func encode(msgType messageType, in interface{}) (*bytes.Buffer, error) { return buf, err } -// GetPrivateIP returns the first private IP address found in a list of -// addresses. -func GetPrivateIP(addresses []net.Addr) (net.IP, error) { - var candidates []net.IP - - // Find private IPv4 address - for _, rawAddr := range addresses { - var ip net.IP - switch addr := rawAddr.(type) { - case *net.IPAddr: - ip = addr.IP - case *net.IPNet: - ip = addr.IP - default: - continue - } - - if ip.To4() == nil { - continue - } - if !IsPrivateIP(ip.String()) { - continue - } - candidates = append(candidates, ip) - } - numIps := len(candidates) - switch numIps { - case 0: - return nil, fmt.Errorf("No private IP address found") - case 1: - return candidates[0], nil - default: - return nil, fmt.Errorf("Multiple private IPs found. Please configure one.") - } -} - // Returns a random offset between 0 and n func randomOffset(n int) int { if n == 0 { @@ -190,9 +95,9 @@ func pushPullScale(interval time.Duration, n int) time.Duration { return time.Duration(multiplier) * interval } -// moveDeadNodes moves all the nodes in the dead state -// to the end of the slice and returns the index of the first dead node. -func moveDeadNodes(nodes []*nodeState) int { +// moveDeadNodes moves nodes that are dead and beyond the gossip to the dead interval +// to the end of the slice and returns the index of the first moved node. +func moveDeadNodes(nodes []*nodeState, gossipToTheDeadTime time.Duration) int { numDead := 0 n := len(nodes) for i := 0; i < n-numDead; i++ { @@ -200,6 +105,11 @@ func moveDeadNodes(nodes []*nodeState) int { continue } + // Respect the gossip to the dead interval + if time.Since(nodes[i].StateChange) <= gossipToTheDeadTime { + continue + } + // Move this node to the end nodes[i], nodes[n-numDead-1] = nodes[n-numDead-1], nodes[i] numDead++ @@ -305,23 +215,6 @@ func decodeCompoundMessage(buf []byte) (trunc int, parts [][]byte, err error) { return } -// Returns if the given IP is in a private block -func IsPrivateIP(ip_str string) bool { - ip := net.ParseIP(ip_str) - for _, priv := range privateBlocks { - if priv.Contains(ip) { - return true - } - } - return false -} - -// Returns if the given IP is in a loopback block -func isLoopbackIP(ip_str string) bool { - ip := net.ParseIP(ip_str) - return loopbackBlock.Contains(ip) -} - // Given a string of the form "host", "host:port", // "ipv6::addr" or "[ipv6::address]:port", // return true if the string includes a port. diff --git a/vendor/github.com/hashicorp/serf/serf/config.go b/vendor/github.com/hashicorp/serf/serf/config.go index 2403cea58731..74f21ffbdf74 100644 --- a/vendor/github.com/hashicorp/serf/serf/config.go +++ b/vendor/github.com/hashicorp/serf/serf/config.go @@ -2,6 +2,7 @@ package serf import ( "io" + "log" "os" "time" @@ -183,6 +184,12 @@ type Config struct { // logs will go to stderr. LogOutput io.Writer + // Logger is a custom logger which you provide. If Logger is set, it will use + // this for the internal logger. If Logger is not set, it will fall back to the + // behavior for using LogOutput. You cannot specify both LogOutput and Logger + // at the same time. + Logger *log.Logger + // SnapshotPath if provided is used to snapshot live nodes as well // as lamport clock values. When Serf is started with a snapshot, // it will attempt to join all the previously known nodes until one diff --git a/vendor/github.com/hashicorp/serf/serf/serf.go b/vendor/github.com/hashicorp/serf/serf/serf.go index 62fef5bc06b3..c7e603b2437f 100644 --- a/vendor/github.com/hashicorp/serf/serf/serf.go +++ b/vendor/github.com/hashicorp/serf/serf/serf.go @@ -10,6 +10,7 @@ import ( "log" "math/rand" "net" + "os" "strconv" "sync" "time" @@ -240,9 +241,23 @@ func Create(conf *Config) (*Serf, error) { conf.ProtocolVersion, ProtocolVersionMin, ProtocolVersionMax) } + if conf.LogOutput != nil && conf.Logger != nil { + return nil, fmt.Errorf("Cannot specify both LogOutput and Logger. Please choose a single log configuration setting.") + } + + logDest := conf.LogOutput + if logDest == nil { + logDest = os.Stderr + } + + logger := conf.Logger + if logger == nil { + logger = log.New(logDest, "", log.LstdFlags) + } + serf := &Serf{ config: conf, - logger: log.New(conf.LogOutput, "", log.LstdFlags), + logger: logger, members: make(map[string]*memberState), queryResponse: make(map[LamportTime]*QueryResponse), shutdownCh: make(chan struct{}), diff --git a/vendor/github.com/hashicorp/serf/serf/snapshot.go b/vendor/github.com/hashicorp/serf/serf/snapshot.go index 44f8a5175ad0..6e1fbd596c13 100644 --- a/vendor/github.com/hashicorp/serf/serf/snapshot.go +++ b/vendor/github.com/hashicorp/serf/serf/snapshot.go @@ -84,7 +84,7 @@ func NewSnapshotter(path string, inCh := make(chan Event, 1024) // Try to open the file - fh, err := os.OpenFile(path, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0755) + fh, err := os.OpenFile(path, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0644) if err != nil { return nil, nil, fmt.Errorf("failed to open snapshot: %v", err) } diff --git a/vendor/github.com/sean-/seed/LICENSE b/vendor/github.com/sean-/seed/LICENSE new file mode 100644 index 000000000000..7471176b8daa --- /dev/null +++ b/vendor/github.com/sean-/seed/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2017 Sean Chittenden +Copyright (c) 2016 Alex Dadgar + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/sean-/seed/README.md b/vendor/github.com/sean-/seed/README.md new file mode 100644 index 000000000000..78c6172ae091 --- /dev/null +++ b/vendor/github.com/sean-/seed/README.md @@ -0,0 +1,44 @@ +# `seed` - Quickly Seed Go's Random Number Generator + +Boiler-plate to securely [seed](https://en.wikipedia.org/wiki/Random_seed) Go's +random number generator (if possible). This library isn't anything fancy, it's +just a canonical way of seeding Go's random number generator. Cribbed from +[`Nomad`](https://github.com/hashicorp/nomad/commit/f89a993ec6b91636a3384dd568898245fbc273a1) +before it was moved into +[`Consul`](https://github.com/hashicorp/consul/commit/d695bcaae6e31ee307c11fdf55bb0bf46ea9fcf4) +and made into a helper function, and now further modularized to be a super +lightweight and reusable library. + +Time is better than +[Go's default seed of `1`](https://golang.org/pkg/math/rand/#Seed), but friends +don't let friends use time as a seed to a random number generator. Use +`seed.MustInit()` instead. + +`seed.Init()` is an idempotent and reentrant call that will return an error if +it can't seed the value the first time it is called. `Init()` is reentrant. + +`seed.MustInit()` is idempotent and reentrant call that will `panic()` if it +can't seed the value the first time it is called. `MustInit()` is reentrant. + +## Usage + +``` +package mypackage + +import ( + "github.com/sean-/seed" +) + +// MustInit will panic() if it is unable to set a high-entropy random seed: +func init() { + seed.MustInit() +} + +// Or if you want to not panic() and can actually handle this error: +func init() { + if ok, err := !seed.Init(); !ok { + // Handle the error + //panic(fmt.Sprintf("Unable to securely seed Go's RNG: %v", err)) + } +} +``` diff --git a/vendor/github.com/sean-/seed/init.go b/vendor/github.com/sean-/seed/init.go new file mode 100644 index 000000000000..b4be01d97f58 --- /dev/null +++ b/vendor/github.com/sean-/seed/init.go @@ -0,0 +1,65 @@ +package seed + +import ( + crand "crypto/rand" + "fmt" + "math" + "math/big" + "math/rand" + "sync" + "time" +) + +var ( + once sync.Once + secure bool + seeded bool +) + +// Init provides best-effort seeding (which is better than running with Go's +// default seed of 1). If `/dev/urandom` is available, Init() will seed Go's +// runtime with entropy from `/dev/urandom` and return true because the runtime +// was securely seeded. If Init() has already initialized the random number or +// it had failed to securely initialize the random number generation, Init() +// will return false. See MustInit(). +func Init() (bool, error) { + var err error + once.Do(func() { + var n *big.Int + n, err = crand.Int(crand.Reader, big.NewInt(math.MaxInt64)) + if err != nil { + rand.Seed(time.Now().UTC().UnixNano()) + return + } + rand.Seed(n.Int64()) + secure = true + seeded = true + }) + return seeded && secure, err +} + +// MustInit provides guaranteed seeding. If `/dev/urandom` is not available, +// MustInit will panic() with an error indicating why reading from +// `/dev/urandom` failed. See Init() +func MustInit() { + once.Do(func() { + n, err := crand.Int(crand.Reader, big.NewInt(math.MaxInt64)) + if err != nil { + panic(fmt.Sprintf("Unable to seed the random number generator: %v", err)) + } + rand.Seed(n.Int64()) + secure = true + seeded = true + }) +} + +// Secure returns true if a cryptographically secure seed was used to +// initialize rand. +func Secure() bool { + return secure +} + +// Seeded returns true if Init has seeded the random number generator. +func Seeded() bool { + return seeded +} diff --git a/vendor/vendor.json b/vendor/vendor.json index 04c90ce7104d..88481b9ab42c 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -474,10 +474,10 @@ "revisionTime": "2016-09-30T03:51:02Z" }, { - "checksumSHA1": "L9iVjsIjeU9Ia1VZUY417ZHBAtc=", + "checksumSHA1": "BGODc7juQbdG3vNXHZG07kt+lKI=", "path": "github.com/hashicorp/go-sockaddr", - "revision": "945b922c7db81f071a1f176e747e9857b76ed7c3", - "revisionTime": "2017-01-17T20:27:00Z" + "revision": "f910dd83c2052566cad78352c33af714358d1372", + "revisionTime": "2017-02-08T07:30:35Z" }, { "checksumSHA1": "lPzwetgfMBtpHqdTPolgejMctVQ=", @@ -582,10 +582,10 @@ "revisionTime": "2015-06-09T07:04:31Z" }, { - "checksumSHA1": "hSoH77pX3FyU6kkYqOOYmf3r55Y=", + "checksumSHA1": "1zk7IeGClUqBo+Phsx89p7fQ/rQ=", "path": "github.com/hashicorp/memberlist", - "revision": "9800c50ab79c002353852a9b1095e9591b161513", - "revisionTime": "2016-12-13T23:44:46Z" + "revision": "23ad4b7d7b38496cd64c241dfd4c60b7794c254a", + "revisionTime": "2017-02-08T21:15:06Z" }, { "checksumSHA1": "qnlqWJYV81ENr61SZk9c65R1mDo=", @@ -625,11 +625,11 @@ "revisionTime": "2016-08-09T01:42:04Z" }, { - "checksumSHA1": "E63/tz2qjNJ6+hyGi9AoYb0sH9s=", + "checksumSHA1": "AZ4RoXStVz6qx38ZMZAyC6Gw3Q4=", "comment": "v0.7.0-66-g6c4672d", "path": "github.com/hashicorp/serf/serf", - "revision": "f85661e5323286a0406cabeb0ad515962c1780b7", - "revisionTime": "2017-02-06T16:55:42Z" + "revision": "c5e26c3704ca774760df65ee8cbb039d9d9ec560", + "revisionTime": "2017-02-08T21:49:39Z" }, { "checksumSHA1": "ZhK6IO2XN81Y+3RAjTcVm1Ic7oU=", @@ -692,6 +692,12 @@ "revision": "9b3edd62028f107d7cabb19353292afd29311a4e", "revisionTime": "2016-07-12T16:32:29Z" }, + { + "checksumSHA1": "A/YUMbGg1LHIeK2+NLZBt+MIAao=", + "path": "github.com/sean-/seed", + "revision": "3c72d44db0c567f7c901f9c5da5fe68392227750", + "revisionTime": "2017-02-08T16:47:21Z" + }, { "checksumSHA1": "9rCHOi3ELajBtQUPWxxmEo/Nalk=", "path": "github.com/shirou/gopsutil",