mirror of https://github.com/fmartingr/nudge.git
138 lines
2.1 KiB
Go
138 lines
2.1 KiB
Go
package pinger
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
"time"
|
|
|
|
ping "github.com/go-ping/ping"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
type pingerIPStatus struct {
|
|
ip string
|
|
ok bool
|
|
}
|
|
|
|
func (s *pingerIPStatus) Ok() bool {
|
|
return s.ok
|
|
}
|
|
|
|
type Pinger struct {
|
|
logger *logrus.Entry
|
|
|
|
ips []string
|
|
interval time.Duration
|
|
status map[string]pingerIPStatus
|
|
statusMutex sync.RWMutex
|
|
statusChan chan pingerIPStatus
|
|
ok bool
|
|
|
|
stop chan struct{}
|
|
stopOnce sync.Once
|
|
}
|
|
|
|
func (p *Pinger) ping(addr string) {
|
|
pinger, err := ping.NewPinger(addr)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
pinger.Count = 3
|
|
status := pingerIPStatus{ip: addr, ok: true}
|
|
err = pinger.Run() // blocks
|
|
if err != nil {
|
|
status.ok = false
|
|
}
|
|
if pinger.Statistics().PacketLoss == 100 {
|
|
status.ok = false
|
|
}
|
|
p.statusChan <- status
|
|
}
|
|
|
|
func (p *Pinger) check() {
|
|
for _, ip := range p.ips {
|
|
go p.ping(ip)
|
|
}
|
|
}
|
|
|
|
func (p *Pinger) Start(ctx context.Context) {
|
|
p.check()
|
|
|
|
go p.process(ctx)
|
|
go p.updater(ctx)
|
|
}
|
|
|
|
func (p *Pinger) process(ctx context.Context) {
|
|
timer := time.NewTicker(p.interval)
|
|
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
|
|
case <-p.stop:
|
|
return
|
|
|
|
case <-timer.C:
|
|
p.check()
|
|
}
|
|
}
|
|
}
|
|
|
|
func (p *Pinger) updater(ctx context.Context) {
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
|
|
case <-p.stop:
|
|
return
|
|
|
|
case status := <-p.statusChan:
|
|
p.statusMutex.Lock()
|
|
p.status[status.ip] = status
|
|
p.statusMutex.Unlock()
|
|
p.updateStatus()
|
|
}
|
|
}
|
|
}
|
|
|
|
func (p *Pinger) Stop() {
|
|
p.stopOnce.Do(func() {
|
|
close(p.stop)
|
|
})
|
|
}
|
|
|
|
func (p *Pinger) Ok() bool {
|
|
return p.ok
|
|
}
|
|
|
|
func (p *Pinger) updateStatus() {
|
|
defer p.statusMutex.RUnlock()
|
|
|
|
fails := 0
|
|
p.statusMutex.RLock()
|
|
for _, s := range p.status {
|
|
if !s.Ok() {
|
|
fails++
|
|
}
|
|
}
|
|
|
|
p.ok = fails != len(p.ips)
|
|
}
|
|
|
|
func NewPinger(logger *logrus.Entry, ips []string, interval int) *Pinger {
|
|
return &Pinger{
|
|
logger: logger.WithField("from", "pinger"),
|
|
|
|
ips: ips,
|
|
interval: time.Duration(interval) * time.Second,
|
|
status: make(map[string]pingerIPStatus, len(ips)),
|
|
statusMutex: sync.RWMutex{},
|
|
statusChan: make(chan pingerIPStatus),
|
|
|
|
stop: make(chan struct{}),
|
|
stopOnce: sync.Once{},
|
|
}
|
|
}
|