Add option to cosume connect events rather than polling to restart deployments

This commit is contained in:
jillianwilson
2021-10-06 14:50:47 -03:00
parent b35c668959
commit a5f4a7a0c1
733 changed files with 86908 additions and 24010 deletions

20
vendor/github.com/schollz/peerdiscovery/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,20 @@
# Binaries for programs and plugins
*.exe
*.dll
*.so
*.dylib
# Test binary, build with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
.glide/
examples/examples
examples/ipv4/go.sum
examples/ipv4/ipv4
examples/ipv6/go.sum
examples/ipv6/ipv6

7
vendor/github.com/schollz/peerdiscovery/Dockerfile generated vendored Normal file
View File

@@ -0,0 +1,7 @@
FROM golang
WORKDIR /peerdiscovery
COPY . .
RUN go build ./examples/ipv4/main.go
CMD ["/peerdiscovery/main"]

21
vendor/github.com/schollz/peerdiscovery/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 Zack
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.

72
vendor/github.com/schollz/peerdiscovery/README.md generated vendored Normal file
View File

@@ -0,0 +1,72 @@
# peerdiscovery
<img src="https://img.shields.io/badge/coverage-89%25-brightgreen.svg?style=flat-square" alt="Code coverage">&nbsp;<a href="https://goreportcard.com/report/github.com/schollz/peerdiscovery"><img src="https://goreportcard.com/badge/github.com/schollz/peerdiscovery?style=flat-square" alt="Go Report"></a>&nbsp;<a href="https://godoc.org/github.com/schollz/peerdiscovery"><img src="http://img.shields.io/badge/godoc-reference-5272B4.svg?style=flat-square" alt="Go Doc"></a>
Pure-go library for cross-platform thread-safe local peer discovery using UDP multicast. I needed to use peer discovery for [croc](https://github.com/schollz/croc) and everything I tried had problems, so I made another one.
## Install
Make sure you have Go 1.5+.
```
go get -u github.com/schollz/peerdiscovery
```
## Usage
The following is a code to find the first peer on the local network and print it out.
```golang
discoveries, _ := peerdiscovery.Discover(peerdiscovery.Settings{Limit: 1})
for _, d := range discoveries {
fmt.Printf("discovered '%s'\n", d.Address)
}
```
Here's the output when running on two computers. (*Run these gifs in sync by hitting Ctl + F5*).
**Computer 1:**
![computer 1](https://user-images.githubusercontent.com/6550035/39165714-ba7167d8-473a-11e8-82b5-fb7401ce2138.gif)
**Computer 2:**
![computer 1](https://user-images.githubusercontent.com/6550035/39165716-ba8db9ec-473a-11e8-96f7-e8c64faac676.gif)
For more examples, see the scanning examples ([ipv4](https://github.com/schollz/peerdiscovery/blob/master/examples/ipv4/main.go) and [ipv6](https://github.com/schollz/peerdiscovery/blob/master/examples/ipv6/main.go)) or [the docs](https://pkg.go.dev/github.com/schollz/peerdiscovery).
## Testing
To test the peer discovery with just one host, one can launch multiple containers. The provided `Dockerfile` will run the example code.
Please make sure to enable [Docker's IPv6 support](https://docs.docker.com/v17.09/engine/userguide/networking/default_network/ipv6/) if you are using IPv6 for peer discovery.
```console
# Build the container, named peertest
$ docker build -t peertest .
# Execute the following command in multiple terminals
$ docker run -t --rm peertest
Scanning for 10 seconds to find LAN peers
100% |████████████████████████████████████████| [9s:0s]Found 1 other computers
0) '172.17.0.2' with payload 'zqrecHipCO'
```
## Contributing
Pull requests are welcome. Feel free to...
- Revise documentation
- Add new features
- Fix bugs
- Suggest improvements
## Thanks
Thanks [@geistesk](https://github.com/geistesk) for adding IPv6 support and a `Notify` func, and helping maintain! Thanks [@Kunde21](https://github.com/Kunde21) for providing a bug fix and massively refactoring the code in a much better way. Thanks [@robpre](https://github.com/robpre) for finding and fixing bugs. Thanks [@shvydky](https://github.com/shvydky) for adding dynamic payloads.
## License
MIT

9
vendor/github.com/schollz/peerdiscovery/go.mod generated vendored Normal file
View File

@@ -0,0 +1,9 @@
module github.com/schollz/peerdiscovery
go 1.13
require (
github.com/stretchr/testify v1.6.1
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b
golang.org/x/sys v0.0.0-20201113135734-0a15ea8d9b02 // indirect
)

24
vendor/github.com/schollz/peerdiscovery/go.sum generated vendored Normal file
View File

@@ -0,0 +1,24 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201113135734-0a15ea8d9b02 h1:5Ftd3YbC/kANXWCBjvppvUmv1BMakgFcBKA7MpYYp4M=
golang.org/x/sys v0.0.0-20201113135734-0a15ea8d9b02/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -0,0 +1,414 @@
package peerdiscovery
import (
"fmt"
"net"
"strconv"
"sync"
"time"
"golang.org/x/net/ipv4"
"golang.org/x/net/ipv6"
)
// IPVersion specifies the version of the Internet Protocol to be used.
type IPVersion uint
const (
IPv4 IPVersion = 4
IPv6 IPVersion = 6
)
// Discovered is the structure of the discovered peers,
// which holds their local address (port removed) and
// a payload if there is one.
type Discovered struct {
// Address is the local address of a discovered peer.
Address string
// Payload is the associated payload from discovered peer.
Payload []byte
}
func (d Discovered) String() string {
return fmt.Sprintf("address: %s, payload: %s", d.Address, d.Payload)
}
// Settings are the settings that can be specified for
// doing peer discovery.
type Settings struct {
// Limit is the number of peers to discover, use < 1 for unlimited.
Limit int
// Port is the port to broadcast on (the peers must also broadcast using the same port).
// The default port is 9999.
Port string
// MulticastAddress specifies the multicast address.
// You should be able to use any of 224.0.0.0/4 or ff00::/8.
// By default it uses the Simple Service Discovery Protocol
// address (239.255.255.250 for IPv4 or ff02::c for IPv6).
MulticastAddress string
// Payload is the bytes that are sent out with each broadcast. Must be short.
Payload []byte
// PayloadFunc is the function that will be called to dynamically generate payload
// before every broadcast. If this pointer is nil `Payload` field will be broadcasted instead.
PayloadFunc func() []byte
// Delay is the amount of time between broadcasts. The default delay is 1 second.
Delay time.Duration
// TimeLimit is the amount of time to spend discovering, if the limit is not reached.
// A negative limit indiciates scanning until the limit was reached or, if an
// unlimited scanning was requested, no timeout.
// The default time limit is 10 seconds.
TimeLimit time.Duration
// StopChan is a channel to stop the peer discvoery immediatley after reception.
StopChan chan struct{}
// AllowSelf will allow discovery the local machine (default false)
AllowSelf bool
// DisableBroadcast will not allow sending out a broadcast
DisableBroadcast bool
// IPVersion specifies the version of the Internet Protocol (default IPv4)
IPVersion IPVersion
// Notify will be called each time a new peer was discovered.
// The default is nil, which means no notification whatsoever.
Notify func(Discovered)
portNum int
multicastAddressNumbers net.IP
}
// peerDiscovery is the object that can do the discovery for finding LAN peers.
type peerDiscovery struct {
settings Settings
received map[string][]byte
sync.RWMutex
}
// initialize returns a new peerDiscovery object which can be used to discover peers.
// The settings are optional. If any setting is not supplied, then defaults are used.
// See the Settings for more information.
func initialize(settings Settings) (p *peerDiscovery, err error) {
p = new(peerDiscovery)
p.Lock()
defer p.Unlock()
// initialize settings
p.settings = settings
// defaults
if p.settings.Port == "" {
p.settings.Port = "9999"
}
if p.settings.IPVersion == 0 {
p.settings.IPVersion = IPv4
}
if p.settings.MulticastAddress == "" {
if p.settings.IPVersion == IPv4 {
p.settings.MulticastAddress = "239.255.255.250"
} else {
p.settings.MulticastAddress = "ff02::c"
}
}
if len(p.settings.Payload) == 0 {
p.settings.Payload = []byte("hi")
}
if p.settings.Delay == 0 {
p.settings.Delay = 1 * time.Second
}
if p.settings.TimeLimit == 0 {
p.settings.TimeLimit = 10 * time.Second
}
if p.settings.StopChan == nil {
p.settings.StopChan = make(chan struct{})
}
p.received = make(map[string][]byte)
p.settings.multicastAddressNumbers = net.ParseIP(p.settings.MulticastAddress)
if p.settings.multicastAddressNumbers == nil {
err = fmt.Errorf("Multicast Address %s could not be converted to an IP",
p.settings.MulticastAddress)
return
}
p.settings.portNum, err = strconv.Atoi(p.settings.Port)
if err != nil {
return
}
return
}
type NetPacketConn interface {
JoinGroup(ifi *net.Interface, group net.Addr) error
SetMulticastInterface(ini *net.Interface) error
SetMulticastTTL(int) error
ReadFrom(buf []byte) (int, net.Addr, error)
WriteTo(buf []byte, dst net.Addr) (int, error)
}
// Discover will use the created settings to scan for LAN peers. It will return
// an array of the discovered peers and their associate payloads. It will not
// return broadcasts sent to itself.
func Discover(settings ...Settings) (discoveries []Discovered, err error) {
s := Settings{}
if len(settings) > 0 {
s = settings[0]
}
p, err := initialize(s)
if err != nil {
return
}
p.RLock()
address := net.JoinHostPort(p.settings.MulticastAddress, p.settings.Port)
portNum := p.settings.portNum
tickerDuration := p.settings.Delay
timeLimit := p.settings.TimeLimit
p.RUnlock()
// get interfaces
ifaces, err := net.Interfaces()
if err != nil {
return
}
// Open up a connection
c, err := net.ListenPacket(fmt.Sprintf("udp%d", p.settings.IPVersion), address)
if err != nil {
return
}
defer c.Close()
group := p.settings.multicastAddressNumbers
// ipv{4,6} have an own PacketConn, which does not implement net.PacketConn
var p2 NetPacketConn
if p.settings.IPVersion == IPv4 {
p2 = PacketConn4{ipv4.NewPacketConn(c)}
} else {
p2 = PacketConn6{ipv6.NewPacketConn(c)}
}
for i := range ifaces {
p2.JoinGroup(&ifaces[i], &net.UDPAddr{IP: group, Port: portNum})
}
go p.listen()
ticker := time.NewTicker(tickerDuration)
defer ticker.Stop()
start := time.Now()
for {
exit := false
p.RLock()
if len(p.received) >= p.settings.Limit && p.settings.Limit > 0 {
exit = true
}
p.RUnlock()
if !s.DisableBroadcast {
payload := p.settings.Payload
if p.settings.PayloadFunc != nil {
payload = p.settings.PayloadFunc()
}
// write to multicast
broadcast(p2, payload, ifaces, &net.UDPAddr{IP: group, Port: portNum})
}
select {
case <-p.settings.StopChan:
exit = true
case <-ticker.C:
}
if exit || timeLimit > 0 && time.Since(start) > timeLimit {
break
}
}
if !s.DisableBroadcast {
payload := p.settings.Payload
if p.settings.PayloadFunc != nil {
payload = p.settings.PayloadFunc()
}
// send out broadcast that is finished
broadcast(p2, payload, ifaces, &net.UDPAddr{IP: group, Port: portNum})
}
p.RLock()
discoveries = make([]Discovered, len(p.received))
i := 0
for ip, payload := range p.received {
discoveries[i] = Discovered{
Address: ip,
Payload: payload,
}
i++
}
p.RUnlock()
return
}
func broadcast(p2 NetPacketConn, payload []byte, ifaces []net.Interface, dst net.Addr) {
for i := range ifaces {
if errMulticast := p2.SetMulticastInterface(&ifaces[i]); errMulticast != nil {
continue
}
p2.SetMulticastTTL(2)
if _, errMulticast := p2.WriteTo([]byte(payload), dst); errMulticast != nil {
continue
}
}
}
const (
// https://en.wikipedia.org/wiki/User_Datagram_Protocol#Packet_structure
maxDatagramSize = 66507
)
// Listen binds to the UDP address and port given and writes packets received
// from that address to a buffer which is passed to a hander
func (p *peerDiscovery) listen() (recievedBytes []byte, err error) {
p.RLock()
address := net.JoinHostPort(p.settings.MulticastAddress, p.settings.Port)
portNum := p.settings.portNum
allowSelf := p.settings.AllowSelf
notify := p.settings.Notify
p.RUnlock()
localIPs := getLocalIPs()
// get interfaces
ifaces, err := net.Interfaces()
if err != nil {
return
}
// log.Println(ifaces)
// Open up a connection
c, err := net.ListenPacket(fmt.Sprintf("udp%d", p.settings.IPVersion), address)
if err != nil {
return
}
defer c.Close()
group := p.settings.multicastAddressNumbers
var p2 NetPacketConn
if p.settings.IPVersion == IPv4 {
p2 = PacketConn4{ipv4.NewPacketConn(c)}
} else {
p2 = PacketConn6{ipv6.NewPacketConn(c)}
}
for i := range ifaces {
p2.JoinGroup(&ifaces[i], &net.UDPAddr{IP: group, Port: portNum})
}
// Loop forever reading from the socket
for {
buffer := make([]byte, maxDatagramSize)
var (
n int
src net.Addr
errRead error
)
n, src, errRead = p2.ReadFrom(buffer)
if errRead != nil {
err = errRead
return
}
srcHost, _, _ := net.SplitHostPort(src.String())
if _, ok := localIPs[srcHost]; ok && !allowSelf {
continue
}
// log.Println(src, hex.Dump(buffer[:n]))
p.Lock()
if _, ok := p.received[srcHost]; !ok {
p.received[srcHost] = buffer[:n]
}
p.Unlock()
if notify != nil {
notify(Discovered{
Address: srcHost,
Payload: buffer[:n],
})
}
p.RLock()
if len(p.received) >= p.settings.Limit && p.settings.Limit > 0 {
p.RUnlock()
break
}
p.RUnlock()
}
return
}
// getLocalIPs returns the local ip address
func getLocalIPs() (ips map[string]struct{}) {
ips = make(map[string]struct{})
ips["localhost"] = struct{}{}
ips["127.0.0.1"] = struct{}{}
ips["::1"] = struct{}{}
ifaces, err := net.Interfaces()
if err != nil {
return
}
for _, iface := range ifaces {
addrs, err := iface.Addrs()
if err != nil {
continue
}
for _, address := range addrs {
ip, _, err := net.ParseCIDR(address.String())
if err != nil {
// log.Printf("Failed to parse %s: %v", address.String(), err)
continue
}
ips[ip.String()+"%"+iface.Name] = struct{}{}
ips[ip.String()] = struct{}{}
}
}
return
}
type PacketConn4 struct {
*ipv4.PacketConn
}
// ReadFrom wraps the ipv4 ReadFrom without a control message
func (pc4 PacketConn4) ReadFrom(buf []byte) (int, net.Addr, error) {
n, _, addr, err := pc4.PacketConn.ReadFrom(buf)
return n, addr, err
}
// WriteTo wraps the ipv4 WriteTo without a control message
func (pc4 PacketConn4) WriteTo(buf []byte, dst net.Addr) (int, error) {
return pc4.PacketConn.WriteTo(buf, nil, dst)
}
type PacketConn6 struct {
*ipv6.PacketConn
}
// ReadFrom wraps the ipv6 ReadFrom without a control message
func (pc6 PacketConn6) ReadFrom(buf []byte) (int, net.Addr, error) {
n, _, addr, err := pc6.PacketConn.ReadFrom(buf)
return n, addr, err
}
// WriteTo wraps the ipv6 WriteTo without a control message
func (pc6 PacketConn6) WriteTo(buf []byte, dst net.Addr) (int, error) {
return pc6.PacketConn.WriteTo(buf, nil, dst)
}
// SetMulticastTTL wraps the hop limit of ipv6
func (pc6 PacketConn6) SetMulticastTTL(i int) error {
return pc6.SetMulticastHopLimit(i)
}