add mvp
This commit is contained in:
parent
9ca9ef1b9f
commit
deb4020df0
7 changed files with 386 additions and 0 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -22,3 +22,6 @@ go.work
|
||||||
|
|
||||||
# vscode Go debugging files
|
# vscode Go debugging files
|
||||||
__debug_*
|
__debug_*
|
||||||
|
|
||||||
|
# vscode editor config
|
||||||
|
.vscode/
|
||||||
|
|
1
VERSION
Normal file
1
VERSION
Normal file
|
@ -0,0 +1 @@
|
||||||
|
0.1.0
|
117
collector/collector.go
Normal file
117
collector/collector.go
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
/*
|
||||||
|
Copyright 2024 Adora Laura Kalb <adora@lila.network>
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package collector
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/netip"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-kit/log"
|
||||||
|
"github.com/go-kit/log/level"
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
"golang.adora.codes/ntppool-exporter/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Metrics struct {
|
||||||
|
NtpppolServerScore prometheus.Gauge
|
||||||
|
}
|
||||||
|
|
||||||
|
type Collector struct {
|
||||||
|
ctx context.Context
|
||||||
|
target netip.Addr
|
||||||
|
logger log.Logger
|
||||||
|
metrics Metrics
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(ctx context.Context, target netip.Addr, logger log.Logger, metrics Metrics) *Collector {
|
||||||
|
return &Collector{ctx: ctx, target: target, logger: logger, metrics: metrics}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Describe implements Prometheus.Collector.
|
||||||
|
func (c Collector) Describe(ch chan<- *prometheus.Desc) {
|
||||||
|
ch <- prometheus.NewDesc("dummy", "dummy", nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect implements Prometheus.Collector.
|
||||||
|
func (c Collector) Collect(ch chan<- prometheus.Metric) {
|
||||||
|
logger := log.With(c.logger, "scraper")
|
||||||
|
level.Debug(logger).Log("msg", "Starting scrape")
|
||||||
|
start := time.Now()
|
||||||
|
c.collect(ch, logger)
|
||||||
|
duration := time.Since(start).Seconds()
|
||||||
|
level.Debug(logger).Log("msg", "Finished scrape", "duration_seconds", duration)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Collector) collect(ch chan<- prometheus.Metric, logger log.Logger) {
|
||||||
|
httpError := false
|
||||||
|
var serverApiScore float64 = 0
|
||||||
|
const apiEndpoint = "https://www.ntppool.org/scores/"
|
||||||
|
const apiQuery = "/json?limit=1&monitor=24"
|
||||||
|
|
||||||
|
url := apiEndpoint + c.target.String() + apiQuery
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
req, err := http.NewRequest("GET", url, nil)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
level.Error(logger).Log("msg", "Error sending HTTP request", "url", url, "message", err)
|
||||||
|
httpError = true
|
||||||
|
}
|
||||||
|
res, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
level.Error(logger).Log("msg", "Error in HTTP response", "status", res.Status)
|
||||||
|
httpError = true
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
|
||||||
|
body, err := io.ReadAll(res.Body)
|
||||||
|
if err != nil {
|
||||||
|
level.Error(logger).Log("msg", "Error reading HTTP response body", "error", err)
|
||||||
|
httpError = true
|
||||||
|
}
|
||||||
|
|
||||||
|
var response models.ApiResponse
|
||||||
|
|
||||||
|
err = json.Unmarshal(body, &response)
|
||||||
|
if err != nil {
|
||||||
|
level.Error(logger).Log("msg", "Error unmarshaling JSON body", "error", err)
|
||||||
|
httpError = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !httpError {
|
||||||
|
serverApiScore = response.History[0].Score
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Test or delete
|
||||||
|
serverScoreMetric := prometheus.NewDesc("ntppool_score",
|
||||||
|
"Shows the server score currently assigned at ntppool.org",
|
||||||
|
nil, prometheus.Labels{"server": c.target.String()})
|
||||||
|
|
||||||
|
// TODO: Test or delete
|
||||||
|
//c.metrics.NtpppolServerScore.Add(serverApiScore)
|
||||||
|
|
||||||
|
// TODO: Test or delete
|
||||||
|
//Write latest value for each metric in the prometheus metric channel.
|
||||||
|
//Note that you can pass CounterValue, GaugeValue, or UntypedValue types here.
|
||||||
|
m1 := prometheus.MustNewConstMetric(serverScoreMetric, prometheus.GaugeValue, serverApiScore)
|
||||||
|
m1 = prometheus.NewMetricWithTimestamp(time.Now(), m1)
|
||||||
|
ch <- m1
|
||||||
|
}
|
17
go.mod
17
go.mod
|
@ -5,11 +5,28 @@ go 1.22.2
|
||||||
require github.com/prometheus/client_golang v1.19.0
|
require github.com/prometheus/client_golang v1.19.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/alecthomas/kingpin/v2 v2.4.0 // indirect
|
||||||
|
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||||
|
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
||||||
|
github.com/go-kit/log v0.2.1 // indirect
|
||||||
|
github.com/go-logfmt/logfmt v0.5.1 // indirect
|
||||||
|
github.com/golang/protobuf v1.5.3 // indirect
|
||||||
|
github.com/jpillora/backoff v1.0.0 // indirect
|
||||||
|
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
|
||||||
github.com/prometheus/client_model v0.5.0 // indirect
|
github.com/prometheus/client_model v0.5.0 // indirect
|
||||||
github.com/prometheus/common v0.48.0 // indirect
|
github.com/prometheus/common v0.48.0 // indirect
|
||||||
|
github.com/prometheus/exporter-toolkit v0.11.0 // indirect
|
||||||
github.com/prometheus/procfs v0.12.0 // indirect
|
github.com/prometheus/procfs v0.12.0 // indirect
|
||||||
|
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
|
||||||
|
golang.org/x/crypto v0.18.0 // indirect
|
||||||
|
golang.org/x/net v0.20.0 // indirect
|
||||||
|
golang.org/x/oauth2 v0.16.0 // indirect
|
||||||
|
golang.org/x/sync v0.5.0 // indirect
|
||||||
golang.org/x/sys v0.16.0 // indirect
|
golang.org/x/sys v0.16.0 // indirect
|
||||||
|
golang.org/x/text v0.14.0 // indirect
|
||||||
|
google.golang.org/appengine v1.6.7 // indirect
|
||||||
google.golang.org/protobuf v1.32.0 // indirect
|
google.golang.org/protobuf v1.32.0 // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
53
go.sum
53
go.sum
|
@ -1,16 +1,69 @@
|
||||||
|
github.com/alecthomas/kingpin/v2 v2.4.0 h1:f48lwail6p8zpO1bC4TxtqACaGqHYA22qkHjHpqDjYY=
|
||||||
|
github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE=
|
||||||
|
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc=
|
||||||
|
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
|
||||||
|
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU=
|
||||||
|
github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
|
||||||
|
github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA=
|
||||||
|
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||||
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
|
github.com/golang/protobuf v1.3.1/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/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
|
||||||
|
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||||
|
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
|
||||||
|
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
|
github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
|
||||||
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
|
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
|
||||||
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
|
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/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
|
||||||
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
|
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
|
||||||
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
|
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
|
||||||
|
github.com/prometheus/exporter-toolkit v0.11.0 h1:yNTsuZ0aNCNFQ3aFTD2uhPOvr4iD7fdBvKPAEGkNf+g=
|
||||||
|
github.com/prometheus/exporter-toolkit v0.11.0/go.mod h1:BVnENhnNecpwoTLiABx7mrPB/OLRIgN74qlQbV+FK1Q=
|
||||||
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
|
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
|
||||||
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
|
||||||
|
github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
|
||||||
|
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||||
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
|
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
|
||||||
|
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||||
|
golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ=
|
||||||
|
golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o=
|
||||||
|
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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||||
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||||
|
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
|
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.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
|
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
|
||||||
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
|
|
154
main.go
Normal file
154
main.go
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
/*
|
||||||
|
Copyright 2024 Adora Laura Kalb <adora@lila.network>
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/netip"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/alecthomas/kingpin/v2"
|
||||||
|
"github.com/go-kit/log"
|
||||||
|
"github.com/go-kit/log/level"
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||||
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
|
"github.com/prometheus/common/promlog"
|
||||||
|
"github.com/prometheus/common/promlog/flag"
|
||||||
|
"github.com/prometheus/common/version"
|
||||||
|
"github.com/prometheus/exporter-toolkit/web"
|
||||||
|
webflag "github.com/prometheus/exporter-toolkit/web/kingpinflag"
|
||||||
|
"golang.adora.codes/ntppool-exporter/collector"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
namespace = "ntppool"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
metricsPath = kingpin.Flag(
|
||||||
|
"web.telemetry-path",
|
||||||
|
"Path under which to expose metrics.",
|
||||||
|
).Default("/metrics").String()
|
||||||
|
//appListenIP = kingpin.Flag("web.listen-address", "Addresses on which to expose metrics and web interface").Default(":43609").String()
|
||||||
|
toolkitFlags = webflag.AddFlags(kingpin.CommandLine, ":43609")
|
||||||
|
|
||||||
|
// Metrics about the SNMP exporter itself.
|
||||||
|
ntppoolRequestErrors = promauto.NewCounter(
|
||||||
|
prometheus.CounterOpts{
|
||||||
|
Namespace: namespace,
|
||||||
|
Name: "request_errors_total",
|
||||||
|
Help: "Errors in requests to the SNMP exporter",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
func handler(w http.ResponseWriter, r *http.Request, logger log.Logger, exporterMetrics collector.Metrics) {
|
||||||
|
|
||||||
|
// /metrics&target=2003%3Adb8%3A%3A1
|
||||||
|
// /metrics&target=1.2.3.4
|
||||||
|
query := r.URL.Query()
|
||||||
|
|
||||||
|
target := query.Get("target")
|
||||||
|
if len(query["target"]) != 1 || target == "" {
|
||||||
|
http.Error(w, "'target' parameter must be specified once", http.StatusBadRequest)
|
||||||
|
ntppoolRequestErrors.Inc()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
targetIP, ipError := netip.ParseAddr(target)
|
||||||
|
if ipError != nil {
|
||||||
|
http.Error(w, "'target' parameter must be a valid IPv4/IPv6 address", http.StatusBadRequest)
|
||||||
|
ntppoolRequestErrors.Inc()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logger = log.With(logger, "target", target)
|
||||||
|
registry := prometheus.NewRegistry()
|
||||||
|
c := collector.New(r.Context(), targetIP, logger, exporterMetrics)
|
||||||
|
registry.MustRegister(c)
|
||||||
|
// Delegate http serving to Prometheus client library, which will call collector.Collect.
|
||||||
|
h := promhttp.HandlerFor(registry, promhttp.HandlerOpts{})
|
||||||
|
h.ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
promlogConfig := &promlog.Config{}
|
||||||
|
flag.AddFlags(kingpin.CommandLine, promlogConfig)
|
||||||
|
kingpin.Version(version.Print("ntppool_exporter"))
|
||||||
|
kingpin.HelpFlag.Short('h')
|
||||||
|
kingpin.Parse()
|
||||||
|
logger := promlog.New(promlogConfig)
|
||||||
|
|
||||||
|
level.Info(logger).Log("msg", "Starting ntppool_exporter", "version", version.Info())
|
||||||
|
level.Info(logger).Log("build_context", version.BuildContext())
|
||||||
|
|
||||||
|
exporterMetrics := collector.Metrics{
|
||||||
|
NtpppolServerScore: promauto.NewGauge(
|
||||||
|
prometheus.GaugeOpts{
|
||||||
|
Namespace: namespace,
|
||||||
|
Name: "server_score",
|
||||||
|
Help: "Current ntppool server score",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
http.HandleFunc(*metricsPath, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
handler(w, r, logger, exporterMetrics)
|
||||||
|
})
|
||||||
|
|
||||||
|
metricsPathValue := *metricsPath
|
||||||
|
|
||||||
|
if *metricsPath != "/" && *metricsPath != "" {
|
||||||
|
landingConfig := web.LandingConfig{
|
||||||
|
Name: "ntppool Exporter",
|
||||||
|
Description: "Prometheus Exporter for SNMP targets",
|
||||||
|
Version: version.Info(),
|
||||||
|
Form: web.LandingForm{
|
||||||
|
Action: metricsPathValue,
|
||||||
|
Inputs: []web.LandingFormInput{
|
||||||
|
{
|
||||||
|
Label: "Target",
|
||||||
|
Type: "text",
|
||||||
|
Name: "target",
|
||||||
|
Placeholder: "X.X.X.X/[::X]",
|
||||||
|
Value: "::1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Links: []web.LandingLinks{
|
||||||
|
{
|
||||||
|
Address: *metricsPath,
|
||||||
|
Text: "Metrics",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
landingPage, err := web.NewLandingPage(landingConfig)
|
||||||
|
if err != nil {
|
||||||
|
level.Error(logger).Log("err", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
http.Handle("/", landingPage)
|
||||||
|
}
|
||||||
|
|
||||||
|
srv := &http.Server{}
|
||||||
|
if err := web.ListenAndServe(srv, toolkitFlags, logger); err != nil {
|
||||||
|
level.Error(logger).Log("msg", "Error starting HTTP server", "err", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
41
models/api_response.go
Normal file
41
models/api_response.go
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
Copyright 2024 Adora Laura Kalb <adora@lila.network>
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package models
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type ApiResponse struct {
|
||||||
|
History []ApiResponseHistory `json:"history"`
|
||||||
|
Monitors []struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Timestamp time.Time `json:"ts"`
|
||||||
|
Score float64 `json:"score"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
} `json:"monitors"`
|
||||||
|
Server struct {
|
||||||
|
IP string `json:"ip"`
|
||||||
|
} `json:"server"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ApiResponseHistory struct {
|
||||||
|
Timestamp int `json:"ts"`
|
||||||
|
Step int `json:"step"`
|
||||||
|
Score float64 `json:"score"`
|
||||||
|
MonitorID int `json:"monitor_id"`
|
||||||
|
}
|
Loading…
Reference in a new issue