130 lines
2.2 KiB
Go
130 lines
2.2 KiB
Go
package health
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net"
|
|
"time"
|
|
|
|
"golang.org/x/net/icmp"
|
|
"golang.org/x/net/ipv4"
|
|
)
|
|
|
|
type PingChecker struct {
|
|
name string
|
|
host string
|
|
count int
|
|
timeout time.Duration
|
|
}
|
|
|
|
func NewPingChecker(name string, config map[string]interface{}) (*PingChecker, error) {
|
|
host, ok := config["host"].(string)
|
|
if !ok {
|
|
return nil, fmt.Errorf("ping checker: missing or invalid 'host' field")
|
|
}
|
|
|
|
count := 1
|
|
if c, ok := config["count"]; ok {
|
|
switch v := c.(type) {
|
|
case int:
|
|
count = v
|
|
case float64:
|
|
count = int(v)
|
|
}
|
|
}
|
|
|
|
timeout := 2 * time.Second
|
|
if t, ok := config["timeout"].(string); ok {
|
|
if d, err := time.ParseDuration(t); err == nil {
|
|
timeout = d
|
|
}
|
|
}
|
|
|
|
return &PingChecker{
|
|
name: name,
|
|
host: host,
|
|
count: count,
|
|
timeout: timeout,
|
|
}, nil
|
|
}
|
|
|
|
func (c *PingChecker) Name() string {
|
|
return c.name
|
|
}
|
|
|
|
func (c *PingChecker) Type() string {
|
|
return "ping"
|
|
}
|
|
|
|
func (c *PingChecker) Check(ctx context.Context) CheckResult {
|
|
addr, err := net.ResolveIPAddr("ip4", c.host)
|
|
if err != nil {
|
|
return CheckResultFailure
|
|
}
|
|
|
|
conn, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0")
|
|
if err != nil {
|
|
return CheckResultFailure
|
|
}
|
|
defer conn.Close()
|
|
|
|
successCount := 0
|
|
for i := 0; i < c.count; i++ {
|
|
select {
|
|
case <-ctx.Done():
|
|
return CheckResultFailure
|
|
default:
|
|
}
|
|
|
|
if c.sendPing(conn, addr) {
|
|
successCount++
|
|
}
|
|
}
|
|
|
|
if successCount > 0 {
|
|
return CheckResultSuccess
|
|
}
|
|
|
|
return CheckResultFailure
|
|
}
|
|
|
|
func (c *PingChecker) sendPing(conn *icmp.PacketConn, addr *net.IPAddr) bool {
|
|
msg := icmp.Message{
|
|
Type: ipv4.ICMPTypeEcho,
|
|
Code: 0,
|
|
Body: &icmp.Echo{
|
|
ID: 1234,
|
|
Seq: 1,
|
|
Data: []byte("go-alived-ping"),
|
|
},
|
|
}
|
|
|
|
msgBytes, err := msg.Marshal(nil)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
if _, err := conn.WriteTo(msgBytes, addr); err != nil {
|
|
return false
|
|
}
|
|
|
|
conn.SetReadDeadline(time.Now().Add(c.timeout))
|
|
|
|
reply := make([]byte, 1500)
|
|
n, _, err := conn.ReadFrom(reply)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
parsedMsg, err := icmp.ParseMessage(ipv4.ICMPTypeEchoReply.Protocol(), reply[:n])
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
if parsedMsg.Type == ipv4.ICMPTypeEchoReply {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|