diff --git a/docs/LICENSE_OF_DEPENDENCIES.md b/docs/LICENSE_OF_DEPENDENCIES.md index 642c79673b18c..14c46448c3b4a 100644 --- a/docs/LICENSE_OF_DEPENDENCIES.md +++ b/docs/LICENSE_OF_DEPENDENCIES.md @@ -51,9 +51,9 @@ following works: - github.com/eclipse/paho.mqtt.golang [Eclipse Public License - v 1.0](https://github.com/eclipse/paho.mqtt.golang/blob/master/LICENSE) - github.com/ericchiang/k8s [Apache License 2.0](https://github.com/ericchiang/k8s/blob/master/LICENSE) - github.com/ghodss/yaml [MIT License](https://github.com/ghodss/yaml/blob/master/LICENSE) -- github.com/glinton/ping [MIT License](https://github.com/glinton/ping/blob/master/LICENSE) - github.com/go-logfmt/logfmt [MIT License](https://github.com/go-logfmt/logfmt/blob/master/LICENSE) - github.com/go-ole/go-ole [MIT License](https://github.com/go-ole/go-ole/blob/master/LICENSE) +- github.com/go-ping/ping [MIT License](https://github.com/go-ping/ping/blob/master/LICENSE) - github.com/go-redis/redis [BSD 2-Clause "Simplified" License](https://github.com/go-redis/redis/blob/master/LICENSE) - github.com/go-sql-driver/mysql [Mozilla Public License 2.0](https://github.com/go-sql-driver/mysql/blob/master/LICENSE) - github.com/goburrow/modbus [BSD 3-Clause "New" or "Revised" License](https://github.com/goburrow/modbus/blob/master/LICENSE) diff --git a/go.mod b/go.mod index bd0ec9345ffbc..45a9a48ba618e 100644 --- a/go.mod +++ b/go.mod @@ -49,9 +49,9 @@ require ( github.com/eclipse/paho.mqtt.golang v1.2.0 github.com/ericchiang/k8s v1.2.0 github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 - github.com/glinton/ping v0.1.4-0.20200311211934-5ac87da8cd96 github.com/go-logfmt/logfmt v0.4.0 github.com/go-ole/go-ole v1.2.1 // indirect + github.com/go-ping/ping v0.0.0-20201115131931-3300c582a663 github.com/go-redis/redis v6.15.9+incompatible github.com/go-sql-driver/mysql v1.5.0 github.com/goburrow/modbus v0.1.0 diff --git a/go.sum b/go.sum index 79588d467c4f9..18fc73ab9df13 100644 --- a/go.sum +++ b/go.sum @@ -208,8 +208,6 @@ github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2H github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 h1:Mn26/9ZMNWSw9C9ERFA1PUxfmGpolnw2v0bKOREu5ew= github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I= -github.com/glinton/ping v0.1.4-0.20200311211934-5ac87da8cd96 h1:YpooqMW354GG47PXNBiaCv6yCQizyP3MXD9NUPrCEQ8= -github.com/glinton/ping v0.1.4-0.20200311211934-5ac87da8cd96/go.mod h1:uY+1eqFUyotrQxF1wYFNtMeHp/swbYRsoGzfcPZ8x3o= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -225,6 +223,8 @@ github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+ github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +github.com/go-ping/ping v0.0.0-20201115131931-3300c582a663 h1:jI2GiiRh+pPbey52EVmbU6kuLiXqwy4CXZ4gwUBj8Y0= +github.com/go-ping/ping v0.0.0-20201115131931-3300c582a663/go.mod h1:35JbSyV/BYqHwwRA6Zr1uVDm1637YlNOU61wI797NPI= github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg= github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= @@ -698,7 +698,6 @@ golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191003171128-d98b1b443823/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= diff --git a/plugins/inputs/ping/ping.go b/plugins/inputs/ping/ping.go index 87f7af8e7489f..f242a80b85400 100644 --- a/plugins/inputs/ping/ping.go +++ b/plugins/inputs/ping/ping.go @@ -1,23 +1,16 @@ package ping import ( - "context" "errors" "fmt" - "log" "math" - "net" - "os/exec" "runtime" - "sort" "strings" "sync" - "sync/atomic" "time" - "github.com/glinton/ping" + "github.com/go-ping/ping" "github.com/influxdata/telegraf" - "github.com/influxdata/telegraf/internal" "github.com/influxdata/telegraf/plugins/inputs" ) @@ -26,13 +19,16 @@ import ( // for unit test purposes (see ping_test.go) type HostPinger func(binary string, timeout float64, args ...string) (string, error) -type HostResolver func(ctx context.Context, ipv6 bool, host string) (*net.IPAddr, error) - -type IsCorrectNetwork func(ip net.IPAddr) bool - type Ping struct { + // wg is used to wait for ping with multiple URLs wg sync.WaitGroup + // Pre-calculated interval and timeout + calcInterval time.Duration + calcTimeout time.Duration + + Log telegraf.Logger `toml:"-"` + // Interval at which to ping (ping -i ) PingInterval float64 `toml:"ping_interval"` @@ -67,11 +63,7 @@ type Ping struct { // host ping function pingHost HostPinger - // resolve host function - resolveHost HostResolver - - // listenAddr is the address associated with the interface defined. - listenAddr string + nativePingFunc NativePingFunc // Calculate the given percentiles when using native method Percentiles []int @@ -134,10 +126,6 @@ func (*Ping) SampleConfig() string { } func (p *Ping) Gather(acc telegraf.Accumulator) error { - if p.Interface != "" && p.listenAddr == "" { - p.listenAddr = getAddr(p.Interface) - } - for _, host := range p.Urls { p.wg.Add(1) go func(host string) { @@ -157,204 +145,113 @@ func (p *Ping) Gather(acc telegraf.Accumulator) error { return nil } -func getAddr(iface string) string { - if addr := net.ParseIP(iface); addr != nil { - return addr.String() - } +type pingStats struct { + ping.Statistics + ttl int +} - ifaces, err := net.Interfaces() - if err != nil { - return "" - } +type NativePingFunc func(destination string) (*pingStats, error) - var ip net.IP - for i := range ifaces { - if ifaces[i].Name == iface { - addrs, err := ifaces[i].Addrs() - if err != nil { - return "" - } - if len(addrs) > 0 { - switch v := addrs[0].(type) { - case *net.IPNet: - ip = v.IP - case *net.IPAddr: - ip = v.IP - } - if len(ip) == 0 { - return "" - } - return ip.String() - } - } - } - - return "" -} +func (p *Ping) nativePing(destination string) (*pingStats, error) { + ps := &pingStats{} -func hostPinger(binary string, timeout float64, args ...string) (string, error) { - bin, err := exec.LookPath(binary) + pinger, err := ping.NewPinger(destination) if err != nil { - return "", err + return nil, fmt.Errorf("Failed to create new pinger: %w", err) } - c := exec.Command(bin, args...) - out, err := internal.CombinedOutputTimeout(c, - time.Second*time.Duration(timeout+5)) - return string(out), err -} -func filterIPs(addrs []net.IPAddr, filterFunc IsCorrectNetwork) []net.IPAddr { - n := 0 - for _, x := range addrs { - if filterFunc(x) { - addrs[n] = x - n++ - } + // Required for windows. Despite the method name, this should work without the need to elevate privileges and has been tested on Windows 10 + if runtime.GOOS == "windows" { + pinger.SetPrivileged(true) } - return addrs[:n] -} -func hostResolver(ctx context.Context, ipv6 bool, destination string) (*net.IPAddr, error) { - resolver := &net.Resolver{} - ips, err := resolver.LookupIPAddr(ctx, destination) + if p.IPv6 { + pinger.SetNetwork("ip6") + } - if err != nil { - return nil, err + pinger.Interval = p.calcInterval + pinger.Timeout = p.calcTimeout + + if p.Deadline > 0 { + // If deadline is set ping exits regardless of how many packets have been sent or received + timer := time.AfterFunc(time.Duration(p.Deadline)*time.Second, func() { + pinger.Stop() + }) + defer timer.Stop() } - if ipv6 { - ips = filterIPs(ips, isV6) - } else { - ips = filterIPs(ips, isV4) + // Get Time to live (TTL) of first response, matching original implementation + once := &sync.Once{} + pinger.OnRecv = func(pkt *ping.Packet) { + once.Do(func() { + ps.ttl = pkt.Ttl + }) } - if len(ips) == 0 { - return nil, errors.New("Cannot resolve ip address") + pinger.Count = p.Count + err = pinger.Run() + if err != nil { + return nil, fmt.Errorf("Failed to run pinger: %w", err) } - return &ips[0], err -} -func isV4(ip net.IPAddr) bool { - return ip.IP.To4() != nil -} + ps.Statistics = *pinger.Statistics() -func isV6(ip net.IPAddr) bool { - return !isV4(ip) + return ps, nil } func (p *Ping) pingToURLNative(destination string, acc telegraf.Accumulator) { - ctx := context.Background() - interval := p.PingInterval - if interval < 0.2 { - interval = 0.2 - } - - timeout := p.Timeout - if timeout == 0 { - timeout = 5 - } - tick := time.NewTicker(time.Duration(interval * float64(time.Second))) - defer tick.Stop() - - if p.Deadline > 0 { - var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ctx, time.Duration(p.Deadline)*time.Second) - defer cancel() - } + tags := map[string]string{"url": destination} + fields := map[string]interface{}{} - host, err := p.resolveHost(ctx, p.IPv6, destination) + stats, err := p.nativePingFunc(destination) if err != nil { - acc.AddFields( - "ping", - map[string]interface{}{"result_code": 1}, - map[string]string{"url": destination}, - ) - acc.AddError(err) + if strings.Contains(err.Error(), "unknown") { + fields["result_code"] = 1 + } else { + fields["result_code"] = 2 + } + acc.AddFields("ping", fields, tags) return } - resps := make(chan *ping.Response) - rsps := []*ping.Response{} - - r := &sync.WaitGroup{} - r.Add(1) - go func() { - for res := range resps { - rsps = append(rsps, res) - } - r.Done() - }() - - wg := &sync.WaitGroup{} - c := ping.Client{} - - var doErr error - var packetsSent int32 - - type sentReq struct { - err error - sent bool + fields = map[string]interface{}{ + "result_code": 0, + "packets_transmitted": stats.PacketsSent, + "packets_received": stats.PacketsRecv, } - sents := make(chan sentReq) - r.Add(1) - go func() { - for sent := range sents { - if sent.err != nil { - doErr = sent.err - } - if sent.sent { - atomic.AddInt32(&packetsSent, 1) - } - } - r.Done() - }() - - for i := 0; i < p.Count; i++ { - select { - case <-ctx.Done(): - goto finish - case <-tick.C: - ctx, cancel := context.WithTimeout(ctx, time.Duration(timeout*float64(time.Second))) - defer cancel() - - wg.Add(1) - go func(seq int) { - defer wg.Done() - resp, err := c.Do(ctx, &ping.Request{ - Dst: net.ParseIP(host.String()), - Src: net.ParseIP(p.listenAddr), - Seq: seq, - }) - - sent := sentReq{err: err, sent: true} - if err != nil { - if strings.Contains(err.Error(), "not permitted") { - sent.sent = false - } - sents <- sent - return - } - - resps <- resp - sents <- sent - }(i + 1) - } + if stats.PacketsSent == 0 { + fields["result_code"] = 2 + acc.AddFields("ping", fields, tags) + return } -finish: - wg.Wait() - close(resps) - close(sents) + if stats.PacketsRecv == 0 { + fields["result_code"] = 1 + fields["percent_packet_loss"] = float64(100) + acc.AddFields("ping", fields, tags) + return + } - r.Wait() + for _, perc := range p.Percentiles { + var value = percentile(durationSlice(stats.Rtts), perc) + var field = fmt.Sprintf("percentile%v_ms", perc) + fields[field] = float64(value.Nanoseconds()) / float64(time.Millisecond) + } - if doErr != nil && strings.Contains(doErr.Error(), "not permitted") { - log.Printf("D! [inputs.ping] %s", doErr.Error()) + // Set TTL only on supported platform. See golang.org/x/net/ipv4/payload_cmsg.go + switch runtime.GOOS { + case "aix", "darwin", "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "solaris": + fields["ttl"] = stats.ttl } - tags, fields := onFin(packetsSent, rsps, doErr, destination, p.Percentiles) + fields["percent_packet_loss"] = float64(stats.PacketLoss) + fields["minimum_response_ms"] = float64(stats.MinRtt) / float64(time.Millisecond) + fields["average_response_ms"] = float64(stats.AvgRtt) / float64(time.Millisecond) + fields["maximum_response_ms"] = float64(stats.MaxRtt) / float64(time.Millisecond) + fields["standard_deviation_ms"] = float64(stats.StdDevRtt) / float64(time.Millisecond) + acc.AddFields("ping", fields, tags) } @@ -366,6 +263,9 @@ func (p durationSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } // R7 from Hyndman and Fan (1996), which matches Excel func percentile(values durationSlice, perc int) time.Duration { + if len(values) == 0 { + return 0 + } if perc < 0 { perc = 0 } @@ -388,91 +288,24 @@ func percentile(values durationSlice, perc int) time.Duration { } } -func onFin(packetsSent int32, resps []*ping.Response, err error, destination string, percentiles []int) (map[string]string, map[string]interface{}) { - packetsRcvd := len(resps) - - tags := map[string]string{"url": destination} - fields := map[string]interface{}{ - "result_code": 0, - "packets_transmitted": packetsSent, - "packets_received": packetsRcvd, - } - - if packetsSent == 0 { - if err != nil { - fields["result_code"] = 2 - } - return tags, fields - } - - if packetsRcvd == 0 { - if err != nil { - fields["result_code"] = 1 - } - fields["percent_packet_loss"] = float64(100) - return tags, fields +// Init ensures the plugin is configured correctly. +func (p *Ping) Init() error { + if p.Count < 1 { + return errors.New("bad number of packets to transmit") } - fields["percent_packet_loss"] = float64(int(packetsSent)-packetsRcvd) / float64(packetsSent) * 100 - ttl := resps[0].TTL - - var min, max, avg, total time.Duration - - if len(percentiles) > 0 { - var rtt []time.Duration - for _, resp := range resps { - rtt = append(rtt, resp.RTT) - total += resp.RTT - } - sort.Sort(durationSlice(rtt)) - min = rtt[0] - max = rtt[len(rtt)-1] - - for _, perc := range percentiles { - var value = percentile(durationSlice(rtt), perc) - var field = fmt.Sprintf("percentile%v_ms", perc) - fields[field] = float64(value.Nanoseconds()) / float64(time.Millisecond) - } + // The interval cannot be below 0.2 seconds, matching ping implementation: https://linux.die.net/man/8/ping + if p.PingInterval < 0.2 { + p.calcInterval = time.Duration(.2 * float64(time.Second)) } else { - min = resps[0].RTT - max = resps[0].RTT - - for _, res := range resps { - if res.RTT < min { - min = res.RTT - } - if res.RTT > max { - max = res.RTT - } - total += res.RTT - } - } - - avg = total / time.Duration(packetsRcvd) - var sumsquares time.Duration - for _, res := range resps { - sumsquares += (res.RTT - avg) * (res.RTT - avg) - } - stdDev := time.Duration(math.Sqrt(float64(sumsquares / time.Duration(packetsRcvd)))) - - // Set TTL only on supported platform. See golang.org/x/net/ipv4/payload_cmsg.go - switch runtime.GOOS { - case "aix", "darwin", "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "solaris": - fields["ttl"] = ttl + p.calcInterval = time.Duration(p.PingInterval * float64(time.Second)) } - fields["minimum_response_ms"] = float64(min.Nanoseconds()) / float64(time.Millisecond) - fields["average_response_ms"] = float64(avg.Nanoseconds()) / float64(time.Millisecond) - fields["maximum_response_ms"] = float64(max.Nanoseconds()) / float64(time.Millisecond) - fields["standard_deviation_ms"] = float64(stdDev.Nanoseconds()) / float64(time.Millisecond) - - return tags, fields -} - -// Init ensures the plugin is configured correctly. -func (p *Ping) Init() error { - if p.Count < 1 { - return errors.New("bad number of packets to transmit") + // If no timeout is given default to 5 seconds, matching original implementation + if p.Timeout == 0 { + p.calcTimeout = time.Duration(5) * time.Second + } else { + p.calcTimeout = time.Duration(p.Timeout) * time.Second } return nil @@ -480,9 +313,7 @@ func (p *Ping) Init() error { func init() { inputs.Add("ping", func() telegraf.Input { - return &Ping{ - pingHost: hostPinger, - resolveHost: hostResolver, + p := &Ping{ PingInterval: 1.0, Count: 1, Timeout: 1.0, @@ -492,5 +323,7 @@ func init() { Arguments: []string{}, Percentiles: []int{}, } + p.nativePingFunc = p.nativePing + return p }) } diff --git a/plugins/inputs/ping/ping_test.go b/plugins/inputs/ping/ping_test.go index 7aadba223e224..0afa53706ab5d 100644 --- a/plugins/inputs/ping/ping_test.go +++ b/plugins/inputs/ping/ping_test.go @@ -5,11 +5,14 @@ package ping import ( "context" "errors" + "fmt" "net" "reflect" "sort" "testing" + "time" + "github.com/go-ping/ping" "github.com/influxdata/telegraf/testutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -403,43 +406,115 @@ func mockHostResolver(ctx context.Context, ipv6 bool, host string) (*net.IPAddr, // Test that Gather function works using native ping func TestPingGatherNative(t *testing.T) { - t.Skip("Skipping test due to permission requirements.") + type test struct { + P *Ping + } - var acc testutil.Accumulator - p := Ping{ + fakePingFunc := func(destination string) (*pingStats, error) { + s := &pingStats{ + Statistics: ping.Statistics{ + PacketsSent: 5, + PacketsRecv: 5, + Rtts: []time.Duration{ + 1 * time.Millisecond, + 2 * time.Millisecond, + 3 * time.Millisecond, + 4 * time.Millisecond, + 5 * time.Millisecond, + }, + }, + ttl: 1, + } + + return s, nil + } + + tests := []test{ + { + P: &Ping{ + Urls: []string{"localhost", "127.0.0.2"}, + Method: "native", + Count: 5, + Percentiles: []int{50, 95, 99}, + nativePingFunc: fakePingFunc, + }, + }, + { + P: &Ping{ + Urls: []string{"localhost", "127.0.0.2"}, + Method: "native", + Count: 5, + PingInterval: 1, + Percentiles: []int{50, 95, 99}, + nativePingFunc: fakePingFunc, + }, + }, + } + + for _, tc := range tests { + var acc testutil.Accumulator + err := tc.P.Init() + require.NoError(t, err) + require.NoError(t, acc.GatherError(tc.P.Gather)) + assert.True(t, acc.HasPoint("ping", map[string]string{"url": "localhost"}, "packets_transmitted", 5)) + assert.True(t, acc.HasPoint("ping", map[string]string{"url": "localhost"}, "packets_received", 5)) + assert.True(t, acc.HasField("ping", "percentile50_ms")) + assert.True(t, acc.HasField("ping", "percentile95_ms")) + assert.True(t, acc.HasField("ping", "percentile99_ms")) + assert.True(t, acc.HasField("ping", "percent_packet_loss")) + assert.True(t, acc.HasField("ping", "minimum_response_ms")) + assert.True(t, acc.HasField("ping", "average_response_ms")) + assert.True(t, acc.HasField("ping", "maximum_response_ms")) + assert.True(t, acc.HasField("ping", "standard_deviation_ms")) + } + +} + +func TestNoPacketsSent(t *testing.T) { + p := &Ping{ Urls: []string{"localhost", "127.0.0.2"}, Method: "native", Count: 5, - resolveHost: mockHostResolver, Percentiles: []int{50, 95, 99}, + nativePingFunc: func(destination string) (*pingStats, error) { + s := &pingStats{ + Statistics: ping.Statistics{ + PacketsSent: 0, + PacketsRecv: 0, + }, + } + + return s, nil + }, } - assert.NoError(t, acc.GatherError(p.Gather)) - assert.True(t, acc.HasPoint("ping", map[string]string{"url": "localhost"}, "packets_transmitted", 5)) - assert.True(t, acc.HasPoint("ping", map[string]string{"url": "localhost"}, "packets_received", 5)) - assert.True(t, acc.HasField("ping", "percentile50_ms")) - assert.True(t, acc.HasField("ping", "percentile95_ms")) - assert.True(t, acc.HasField("ping", "percentile99_ms")) -} - -func mockHostResolverError(ctx context.Context, ipv6 bool, host string) (*net.IPAddr, error) { - return nil, errors.New("myMock error") + var testAcc testutil.Accumulator + err := p.Init() + require.NoError(t, err) + p.pingToURLNative("localhost", &testAcc) + require.Zero(t, testAcc.Errors) + require.True(t, testAcc.HasField("ping", "result_code")) + require.Equal(t, 2, testAcc.Metrics[0].Fields["result_code"]) } // Test failed DNS resolutions func TestDNSLookupError(t *testing.T) { - if testing.Short() { - t.Skip("Skipping test due to permission requirements.") - } - - var acc testutil.Accumulator - p := Ping{ - Urls: []string{"localhost"}, - Method: "native", - IPv6: false, - resolveHost: mockHostResolverError, + p := &Ping{ + Count: 1, + Log: testutil.Logger{}, + Urls: []string{"localhost"}, + Method: "native", + IPv6: false, + nativePingFunc: func(destination string) (*pingStats, error) { + return nil, fmt.Errorf("unknown") + }, } - acc.GatherError(p.Gather) - assert.True(t, len(acc.Errors) > 0) + var testAcc testutil.Accumulator + err := p.Init() + require.NoError(t, err) + p.pingToURLNative("localhost", &testAcc) + require.Zero(t, testAcc.Errors) + require.True(t, testAcc.HasField("ping", "result_code")) + require.Equal(t, 1, testAcc.Metrics[0].Fields["result_code"]) }