nudge/internal/pinger/main.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{},
}
}