diff --git a/.goreleaser.yaml b/.goreleaser.yaml index ddaf913..f5e7d46 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -56,6 +56,7 @@ dockers: - "--label=org.opencontainers.image.description='Yet another prometheus ping exporter'" - "--label=org.opencontainers.image.licenses=MIT" - "--platform=linux/amd64" + dockerfile: "docker/Dockerfile" - image_templates: - "ghcr.io/wbollock/{{.ProjectName}}:{{ .Tag }}-linux-arm64" use: buildx @@ -71,6 +72,7 @@ dockers: - "--label=org.opencontainers.image.description='Yet another prometheus ping exporter'" - "--label=org.opencontainers.image.licenses=MIT" - "--platform=linux/arm64" + dockerfile: "docker/Dockerfile" nfpms: - id: default formats: diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index aaf6956..0000000 --- a/Dockerfile +++ /dev/null @@ -1,13 +0,0 @@ -# taken from https://github.com/prometheus/haproxy_exporter/blob/d4aba878f043fd3ad0bcacd0149e7d75e67c0faa/Dockerfile -ARG ARCH="amd64" -ARG OS="linux" -# they don't tag versions only latest -FROM quay.io/prometheus/busybox-${OS}-${ARCH}:latest -# https://github.com/prometheus/busybox -LABEL maintainer="Will Bollock " - -COPY ping_exporter /bin/ping_exporter - -EXPOSE 9927 -USER nobody -ENTRYPOINT [ "/bin/ping_exporter" ] diff --git a/README.md b/README.md index 45c9fe8..1c08160 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ This ping exporter is simple and fills a gap in the ping exporter market just by - [Binary](#binary) - [Source](#source) - [Other Ping Exporters](#other-ping-exporters) + - [Releasing](#releasing) - [Contributors](#contributors) ## Parameters @@ -41,16 +42,16 @@ This ping exporter is simple and fills a gap in the ping exporter market just by ### /probe -| Metric Name | Type | Description | -| ---------------------- | ----- | ------------------------------------------------------ | -| ping_duration_seconds | gauge | Returns how long the probe took to complete in seconds | -| ping_loss_ratio | gauge | Packet loss from 0 to 100 | -| ping_rtt_avg_seconds | gauge | Mean round trip time | -| ping_rtt_max_seconds | gauge | Worst round trip time | -| ping_rtt_min_seconds | gauge | Best round trip time | -| ping_rtt_std_deviation | gauge | Standard deviation | -| ping_success | gauge | Returns whether the ping succeeded | -| ping_timeout | gauge | Returns whether the ping failed by timeout | +| Metric Name | Type | Description | +| ---------------------- | ----- | ----------------------------------------------------------------------------- | +| ping_duration_seconds | gauge | Returns how long the probe took to complete in seconds | +| ping_loss_ratio | gauge | Packet loss from 0 to 100 | +| ping_rtt_avg_seconds | gauge | Mean round trip time | +| ping_rtt_max_seconds | gauge | Worst round trip time | +| ping_rtt_min_seconds | gauge | Best round trip time | +| ping_rtt_std_deviation | gauge | Standard deviation | +| ping_success | gauge | Returns whether the ping succeeded (if any packet returns this is successful) | +| ping_timeout | gauge | Returns whether the ping failed by timeout | ### /metrics @@ -143,6 +144,13 @@ There are many other good ping exporters, please give them a look too: * [smokeping_prober by SuperQ](https://github.com/SuperQ/smokeping_prober) * [ping-exporter by knsd](https://github.com/knsd/ping-exporter) +## Releasing + +1. Merge commits to main. +2. Tag release `git tag -a v1.0.X -m "message"` +3. `git push origin v1.0.X` +4. `goreleaser release` + ## Contributors Special thanks to [stalloneclone](https://github.com/stalloneclone) for supplying the idea and initial code for the project! diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..4ef40fb --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,17 @@ +ARG ARCH="amd64" +ARG OS="linux" +FROM golang:alpine3.18 AS builderimage +LABEL maintainer="Will Bollock " +WORKDIR /go/src/ping-exporter +COPY . . +RUN go build -o ping-exporter cmd/main.go + +################################################################### + +FROM golang:alpine3.18 +COPY --from=builderimage /go/src/ping-exporter/ping-exporter /app/ +WORKDIR /app + +EXPOSE 9141 +USER nobody +ENTRYPOINT [ "./ping-exporter" ] diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml new file mode 100644 index 0000000..ef7cbcb --- /dev/null +++ b/docker/docker-compose.yaml @@ -0,0 +1,25 @@ +version: "3.3" +networks: + prometheus-ping-exporter: +services: + prometheus-ping-exporter: + build: + dockerfile: Dockerfile + command: + - --log.level=debug + networks: + - prometheus-ping-exporter + ports: + - 9141:9141 + restart: always + # privileged: true + # icmp doesnt work well with this compose stack + prometheus: + image: prom/prometheus + volumes: + - ./prometheus.yml:/etc/prometheus/prometheus.yml + ports: + - "9090:9090" + networks: + - prometheus-ping-exporter + restart: always diff --git a/docker/prometheus.yml b/docker/prometheus.yml new file mode 100644 index 0000000..92aa195 --- /dev/null +++ b/docker/prometheus.yml @@ -0,0 +1,39 @@ +global: + scrape_interval: 15s + +scrape_configs: + - job_name: 'ping-exporter-health' + static_configs: + - targets: ["prometheus-ping-exporter:9141"] + - job_name: 'prober' + metrics_path: '/probe' + scrape_interval: 15s + scrape_timeout: 15s + params: + prot: ['4'] + count: ['5'] + interval: ['1s'] + packet: ['udp'] + timeout: ['10s'] + honor_labels: True + static_configs: + - targets: + - google.com + - linode.com + - wboll.dev + relabel_configs: + # Set the exporter's target + - source_labels: [__address__] + target_label: __param_target + # Set address label to instance + - source_labels: [__address__] + target_label: instance + # Actually talk to the blackbox exporter though + - target_label: __address__ + replacement: prometheus-ping-exporter:9141 + # If we set a custom instance label, write it to the + # expected instance label + - source_labels: [__instance] + target_label: instance + regex: '(.+)' + replacement: '${1}' diff --git a/go.mod b/go.mod index 4b0f93e..9442f08 100644 --- a/go.mod +++ b/go.mod @@ -11,14 +11,13 @@ require ( require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/golang/protobuf v1.5.3 // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect - github.com/prometheus/common v0.44.0 // indirect - github.com/prometheus/procfs v0.11.1 // indirect - golang.org/x/net v0.11.0 // indirect - golang.org/x/sync v0.3.0 // indirect - golang.org/x/sys v0.11.0 // indirect + github.com/google/uuid v1.4.0 // indirect + github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect + github.com/prometheus/client_model v0.5.0 // indirect + github.com/prometheus/common v0.45.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect + golang.org/x/net v0.19.0 // indirect + golang.org/x/sync v0.5.0 // indirect + golang.org/x/sys v0.15.0 // indirect google.golang.org/protobuf v1.31.0 // indirect ) diff --git a/go.sum b/go.sum index f0da615..3204ccc 100644 --- a/go.sum +++ b/go.sum @@ -5,45 +5,40 @@ github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus-community/pro-bing v0.3.0 h1:SFT6gHqXwbItEDJhTkzPWVqU6CLEtqEfNAPp47RUON4= github.com/prometheus-community/pro-bing v0.3.0/go.mod h1:p9dLb9zdmv+eLxWfCT6jESWuDrS+YzpPkQBgysQF8a0= github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= -github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM= -github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= -github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= -github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= -github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= -github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= +github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= +github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU= -golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/collector/icmp_collector.go b/internal/collector/icmp_collector.go index 2dffb8e..3b7b7b7 100644 --- a/internal/collector/icmp_collector.go +++ b/internal/collector/icmp_collector.go @@ -1,6 +1,7 @@ package collector import ( + "math" "net/http" "strconv" "strings" @@ -190,9 +191,13 @@ func PingHandler(w http.ResponseWriter, r *http.Request) { } pinger.OnFinish = func(stats *probing.Statistics) { - if pinger.Count == pinger.PacketsSent { + log.Debugf("OnFinish: PacketsSent=%d, PacketsRecv=%d, PacketLoss=%f%%, MinRtt=%v, AvgRtt=%v, MaxRtt=%v, StdDevRtt=%v, Duration=%v", + pinger.PacketsSent, pinger.PacketsRecv, stats.PacketLoss, stats.MinRtt, stats.AvgRtt, stats.MaxRtt, stats.StdDevRtt, time.Since(start)) + + const tolerance = 0.001 // tolerance for easier floating point comparisons + // success declared if we sent as many packets as intended, we got back all packets sent, and packet loss is not equal to 100% + if pinger.Count == pinger.PacketsRecv && math.Abs(stats.PacketLoss-100) > tolerance { // no error will be raised if we reach a timeout - // but if we managed to send as many packets as intended, declare success // https://github.com/prometheus-community/pro-bing/issues/70 pingSuccessGauge.Set(1) pingTimeoutGauge.Set(0) diff --git a/test/integration_test.go b/test/integration_test.go index 3d89f54..e71c58d 100644 --- a/test/integration_test.go +++ b/test/integration_test.go @@ -87,6 +87,19 @@ func TestPingExporterProbeTimeout(t *testing.T) { validateResponse(t, resp, "ping_success 0", "ping_timeout 1") } +func TestPingExporterDNSFailure(t *testing.T) { + server := setupTestServer() + defer server.Close() + + resp, err := http.Get(server.URL + "/probe?target=invalidhostnamethatdoesntresolve&packet=udp&count=1&timeout=1s") + if err != nil { + t.Fatalf("Failed to send GET request: %v", err) + } + defer resp.Body.Close() + + validateResponse(t, resp, "ping_success 0") +} + func BenchmarkPingExporterProbeEndpoint(b *testing.B) { server := setupTestServer() defer server.Close()