wip v1.0.0
This commit is contained in:
129
internal/health/ping.go
Normal file
129
internal/health/ping.go
Normal file
@@ -0,0 +1,129 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user