aaisp-quota-exporter/main.go

118 lines
2.5 KiB
Go

package main
import (
"crypto/tls"
"encoding/json"
"errors"
"flag"
"fmt"
"net/http"
"os"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
const (
period = 5 * time.Minute
timeout = 10 * time.Second
api = "https://quota.aa.net.uk"
)
var (
cl *http.Client
bind = flag.String("bind", ":9100", "[host]:port to bind to.")
monthlyQuotaBytes = promauto.NewGauge(prometheus.GaugeOpts{
Name: "aaisp_quota_monthly_bytes",
Help: "Monthly quota provided by AAISP.",
})
quotaRemainingBytes = promauto.NewGauge(prometheus.GaugeOpts{
Name: "aaisp_quota_remaining_bytes",
Help: "Number of bytes remaining on this month's AAISP quota.",
})
)
func main() {
flag.Parse()
cl = &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
MinVersion: tls.VersionTLS12,
},
},
Timeout: timeout,
}
go collect()
http.Handle("/metrics", promhttp.Handler())
err := http.ListenAndServe(*bind, nil)
switch {
case errors.Is(err, http.ErrServerClosed), err == nil:
// clean shutdown
default:
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
func collect() {
for {
quota, err := getQuota()
if err != nil {
fmt.Fprintln(os.Stderr, err)
} else {
monthlyQuotaBytes.Set(quota.MonthlyQuotaBytes)
quotaRemainingBytes.Set(quota.QuotaRemainingBytes)
}
now := time.Now()
align := now.Truncate(period).Add(period)
s := align.Sub(now)
if s > 0 {
time.Sleep(s)
}
}
}
type quotaResponse struct {
Error string `json:"error"`
MonthlyQuotaBytes float64 `json:"monthly_quota"`
QuotaRemainingBytes float64 `json:"quota_remaining"`
}
func getQuota() (quotaResponse, error) {
req, err := http.NewRequest(http.MethodGet, api, nil)
if err != nil {
return quotaResponse{}, err
}
req.Header.Set("Accept", "application/json")
req.Header.Set("User-Agent", "src.lwithers.me.uk/go/aaisp-quota-exporter v1.0")
resp, err := cl.Do(req)
if err != nil {
return quotaResponse{}, err
}
defer resp.Body.Close()
var q quotaResponse
dec := json.NewDecoder(resp.Body)
if err := dec.Decode(&q); err != nil {
return quotaResponse{}, fmt.Errorf("%s: could not decode response: %w", api, err)
}
if q.Error != "" {
return quotaResponse{}, fmt.Errorf("%s: reported error: %q", api, q.Error)
}
if q.MonthlyQuotaBytes < 1 {
return quotaResponse{}, fmt.Errorf("%s: returned zero values", api)
}
return q, nil
}