Add config & default values & restructure repo
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Tom Neuber 2024-03-13 20:59:43 +01:00
parent 314305b2fc
commit 9e68aeedd3
Signed by: tom
GPG Key ID: F17EFE4272D89FF6
9 changed files with 308 additions and 237 deletions

View File

@ -9,7 +9,7 @@ RUN apt-get update --yes && \
apt-get install --yes build-essential
# Build the actual binary
RUN CGO_ENABLED=0 go build -o sb-server-monitor ./cmd/sb-server-monitor
RUN CGO_ENABLED=0 go build -o sb-server-monitor main.go
# -- -- -- -- -- --

View File

@ -1,67 +0,0 @@
package main
import (
"context"
"flag"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
type application struct {
server *http.Server
interval time.Duration
listenAddress string
}
func (app *application) webListener() {
log.Printf("listening on %s", app.listenAddress)
http.Handle("/", http.RedirectHandler("/metrics", http.StatusPermanentRedirect))
http.Handle("/metrics", promhttp.Handler())
if err := app.server.ListenAndServe(); err != nil {
log.Println(err)
}
}
var app = application{}
func init() {
flag.DurationVar(&app.interval, "interval", 30*time.Second, "refresh interval for metrics (in seconds)")
flag.StringVar(&app.listenAddress, "web.listen-address", ":9192", "address to use for the metrics server")
app.server = &http.Server{
Handler: nil,
Addr: app.listenAddress,
}
}
func main() {
signals := make(chan os.Signal, 1)
signal.Notify(signals, syscall.SIGTERM)
signal.Notify(signals, syscall.SIGINT)
ctx, cancelWorkers := context.WithCancel(context.Background())
done := make(chan interface{})
go app.webListener()
log.Printf("start worker")
go app.refreshWorker(ctx, done)
<-signals
cancelWorkers()
if err := app.server.Shutdown(context.Background()); err != nil {
log.Println(err)
}
log.Println("sb-server-monitor exporter stopped")
}

View File

@ -1,86 +0,0 @@
package main
import "github.com/prometheus/client_golang/prometheus"
const (
namespace = "sb_server_monitor"
MetricLabelServerID = "id"
MetricLabelServerName = "name"
MetricLabelServerCategory = "category"
MetricLabelServerCPU = "cpu"
MetricLabelServerDrives = "drives"
MetricLabelServerMemory = "memory"
MetricLabelServerECC = "ecc"
MetricLabelServerHighIO = "highio"
MetricLabelServerDatacenter = "datacenter"
MetricLabelServerDescription = "description"
MetricLabelServerDist = "dist"
MetricLabelServerInformation = "information"
MetricLabelServerPrice = "price"
MetricLabelServerFixedPrice = "fixed_price"
MetricLabelServerNextReduce = "next_reduce"
MetricLabelServerNextReduceTimestamp = "next_reduce_timestamp"
MetricLabelServerGeneralDrives = "general_drives"
MetricLabelServerHDDs = "hdds"
MetricLabelServerSATAs = "sata"
MetricLabelServerNVMes = "nvme"
MetricLabelServerSetupPrice = "setup_price"
MetricLabelServerSpecials = "specials"
MetricLabelServerBandwith = "bandwith"
MetricLabelServerTraffic = "traffic"
)
var (
metricServerCount = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Namespace: namespace,
Subsystem: "server",
Name: "current_available",
Help: "total number of all current available servers",
},
[]string{
MetricLabelServerCategory,
},
)
metricServers = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Namespace: namespace,
Name: "servers",
Help: "list all servers with some useful information as labels",
},
[]string{
MetricLabelServerID,
MetricLabelServerName,
MetricLabelServerCategory,
MetricLabelServerCPU,
MetricLabelServerDrives,
MetricLabelServerMemory,
MetricLabelServerECC,
MetricLabelServerHighIO,
MetricLabelServerDatacenter,
MetricLabelServerDescription,
MetricLabelServerDist,
MetricLabelServerInformation,
MetricLabelServerPrice,
MetricLabelServerFixedPrice,
MetricLabelServerNextReduce,
MetricLabelServerNextReduceTimestamp,
MetricLabelServerGeneralDrives,
MetricLabelServerHDDs,
MetricLabelServerSATAs,
MetricLabelServerNVMes,
MetricLabelServerSetupPrice,
MetricLabelServerSpecials,
MetricLabelServerBandwith,
MetricLabelServerTraffic,
},
)
)
func init() {
prometheus.MustRegister(
metricServerCount,
metricServers,
)
}

View File

@ -1,82 +0,0 @@
package main
import (
"context"
"fmt"
"log"
"time"
"git.ar21.de/yolokube/sb-server-monitor/hrobot"
"github.com/prometheus/client_golang/prometheus"
)
func (app *application) refreshWorker(ctx context.Context, done chan<- interface{}) {
var sleepCounter int
client := hrobot.NewClient()
for {
select {
case <-ctx.Done():
done <- nil
return
default:
if sleepCounter/int(app.interval.Seconds()) == 0 {
break
}
log.Println("Fetch servers")
sleepCounter = 0
servers, err := client.SbServer.List(context.Background())
if err != nil {
log.Println(err)
break
}
log.Printf("found %d servers", len(servers))
assignParametersToMetricServers(servers)
assignParametersToMetricServerCount(servers[0].Category, len(servers))
}
sleepCounter++
time.Sleep(time.Second)
}
}
func assignParametersToMetricServerCount(category string, value int) {
metricServerCount.With(
prometheus.Labels{
MetricLabelServerCategory: category,
},
).Set(float64(value))
}
func assignParametersToMetricServers(servers []*hrobot.SbServer) {
for _, server := range servers {
metricServers.With(
prometheus.Labels{
MetricLabelServerID: fmt.Sprint(server.ID),
MetricLabelServerName: server.Name,
MetricLabelServerCategory: server.Category,
MetricLabelServerCPU: server.Hardware.CPU,
MetricLabelServerDrives: fmt.Sprint(server.Hardware.Drives),
MetricLabelServerMemory: fmt.Sprint(server.Hardware.Memory),
MetricLabelServerECC: fmt.Sprint(server.Hardware.ECC),
MetricLabelServerHighIO: fmt.Sprint(server.Hardware.HighIO),
MetricLabelServerDatacenter: server.Datacenter,
MetricLabelServerDescription: fmt.Sprint(server.Description),
MetricLabelServerDist: fmt.Sprint(server.Dist),
MetricLabelServerInformation: fmt.Sprint(server.Information),
MetricLabelServerPrice: fmt.Sprint(server.PriceInformation.Price),
MetricLabelServerFixedPrice: fmt.Sprint(server.PriceInformation.FixedPrice),
MetricLabelServerNextReduce: fmt.Sprint(server.PriceInformation.NextReduce),
MetricLabelServerNextReduceTimestamp: fmt.Sprint(server.PriceInformation.NextReduceTimestamp),
MetricLabelServerGeneralDrives: fmt.Sprint(server.DiskData.General),
MetricLabelServerHDDs: fmt.Sprint(server.DiskData.HDD),
MetricLabelServerSATAs: fmt.Sprint(server.DiskData.SATA),
MetricLabelServerNVMes: fmt.Sprint(server.DiskData.NVMe),
MetricLabelServerSetupPrice: fmt.Sprint(server.PriceInformation.SetupPrice),
MetricLabelServerSpecials: fmt.Sprint(server.Specials),
MetricLabelServerBandwith: fmt.Sprint(server.Network.Bandwidth),
MetricLabelServerTraffic: server.Network.Traffic,
},
).Set(float64(server.PriceInformation.Price))
}
}

View File

@ -0,0 +1,85 @@
package sbservermonitor
import (
"fmt"
"log"
"os"
"git.ar21.de/yolokube/sb-server-monitor/hrobot"
"github.com/prometheus/client_golang/prometheus"
)
type Collector struct {
exitOnRequestError bool
lastError error
requestErrorCount int
Data []*hrobot.SbServer
}
func NewExporter(exitOnRequestError bool) *Collector {
return &Collector{
exitOnRequestError: exitOnRequestError,
lastError: nil,
requestErrorCount: 0,
}
}
func (c *Collector) Describe(ch chan<- *prometheus.Desc) {
ch <- metricServerCount
ch <- metricServers
}
func (c *Collector) Collect(ch chan<- prometheus.Metric) {
if len(c.Data) == 0 {
log.Print("no data existing")
if c.exitOnRequestError {
os.Exit(1)
}
}
c.collectServersMetric(ch, c.Data)
c.collectServerCountMetric(ch, c.Data[0].Category, len(c.Data))
}
func (c *Collector) collectServerCountMetric(ch chan<- prometheus.Metric, category string, value int) {
ch <- prometheus.MustNewConstMetric(
metricServerCount,
prometheus.GaugeValue,
float64(value),
category,
)
}
func (c *Collector) collectServersMetric(ch chan<- prometheus.Metric, servers []*hrobot.SbServer) {
for _, server := range servers {
ch <- prometheus.MustNewConstMetric(
metricServers,
prometheus.GaugeValue,
float64(server.PriceInformation.Price),
fmt.Sprint(server.ID),
server.Name,
server.Category,
server.Hardware.CPU,
fmt.Sprint(server.Hardware.Drives),
fmt.Sprint(server.Hardware.Memory),
fmt.Sprint(server.Hardware.ECC),
fmt.Sprint(server.Hardware.HighIO),
server.Datacenter,
fmt.Sprint(server.Description),
fmt.Sprint(server.Dist),
fmt.Sprint(server.Information),
fmt.Sprint(server.PriceInformation.Price),
fmt.Sprint(server.PriceInformation.FixedPrice),
fmt.Sprint(server.PriceInformation.NextReduce),
fmt.Sprint(server.PriceInformation.NextReduceTimestamp),
fmt.Sprint(server.DiskData.General),
fmt.Sprint(server.DiskData.HDD),
fmt.Sprint(server.DiskData.SATA),
fmt.Sprint(server.DiskData.NVMe),
fmt.Sprint(server.PriceInformation.SetupPrice),
fmt.Sprint(server.Specials),
fmt.Sprint(server.Network.Bandwidth),
server.Network.Traffic,
)
}
}

View File

@ -0,0 +1,85 @@
package sbservermonitor
import (
"github.com/prometheus/client_golang/prometheus"
)
const (
namespace = "sb_server_monitor"
metricLabelServerID = "id"
metricLabelServerName = "name"
metricLabelServerCategory = "category"
metricLabelServerCPU = "cpu"
metricLabelServerDrives = "drives"
metricLabelServerMemory = "memory"
metricLabelServerECC = "ecc"
metricLabelServerHighIO = "highio"
metricLabelServerDatacenter = "datacenter"
metricLabelServerDescription = "description"
metricLabelServerDist = "dist"
metricLabelServerInformation = "information"
metricLabelServerPrice = "price"
metricLabelServerFixedPrice = "fixed_price"
metricLabelServerNextReduce = "next_reduce"
metricLabelServerNextReduceTimestamp = "next_reduce_timestamp"
metricLabelServerGeneralDrives = "general_drives"
metricLabelServerHDDs = "hdds"
metricLabelServerSATAs = "sata"
metricLabelServerNVMes = "nvme"
metricLabelServerSetupPrice = "setup_price"
metricLabelServerSpecials = "specials"
metricLabelServerBandwidth = "bandwidth"
metricLabelServerTraffic = "traffic"
)
var (
metricServerCount = prometheus.NewDesc(
prometheus.BuildFQName(
namespace,
"server",
"current_available",
),
"total number of all current available servers",
[]string{
metricLabelServerCategory,
},
nil,
)
metricServers = prometheus.NewDesc(
prometheus.BuildFQName(
namespace,
"",
"servers",
),
"list all servers with some useful information as labels",
[]string{
metricLabelServerID,
metricLabelServerName,
metricLabelServerCategory,
metricLabelServerCPU,
metricLabelServerDrives,
metricLabelServerMemory,
metricLabelServerECC,
metricLabelServerHighIO,
metricLabelServerDatacenter,
metricLabelServerDescription,
metricLabelServerDist,
metricLabelServerInformation,
metricLabelServerPrice,
metricLabelServerFixedPrice,
metricLabelServerNextReduce,
metricLabelServerNextReduceTimestamp,
metricLabelServerGeneralDrives,
metricLabelServerHDDs,
metricLabelServerSATAs,
metricLabelServerNVMes,
metricLabelServerSetupPrice,
metricLabelServerSpecials,
metricLabelServerBandwidth,
metricLabelServerTraffic,
},
nil,
)
)

7
go.mod
View File

@ -2,14 +2,19 @@ module git.ar21.de/yolokube/sb-server-monitor
go 1.22.1
require github.com/prometheus/client_golang v1.19.0
require (
github.com/alecthomas/kingpin/v2 v2.4.0
github.com/prometheus/client_golang v1.19.0
)
require (
github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/prometheus/client_model v0.6.0 // indirect
github.com/prometheus/common v0.49.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
golang.org/x/sys v0.18.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
)

17
go.sum
View File

@ -1,11 +1,18 @@
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-20231202071711-9a357b53e9c9 h1:ez/4by2iGztzR4L0zgAOR8lTQK9VlyBVVd7G4omaOQs=
github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
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/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
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/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
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/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_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos=
@ -14,7 +21,17 @@ github.com/prometheus/common v0.49.0 h1:ToNTdK4zSnPVJmh698mGFkDor9wBI/iGaJy5dbH1
github.com/prometheus/common v0.49.0/go.mod h1:Kxm+EULxRbUkjGU6WFsQqo3ORzB4tyKvlWFOE9mB2sE=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
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/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
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/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.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.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

114
main.go Normal file
View File

@ -0,0 +1,114 @@
package main
import (
"context"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
sbservermonitor "git.ar21.de/yolokube/sb-server-monitor/collector/sb-server-monitor"
"git.ar21.de/yolokube/sb-server-monitor/hrobot"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
type application struct {
collector *sbservermonitor.Collector
server *http.Server
config *appSettings
}
func (app *application) refreshWorker(ctx context.Context, done chan<- interface{}) {
sleepCounter := int(app.config.scrapeInterval)
client := hrobot.NewClient()
for {
select {
case <-ctx.Done():
done <- nil
return
default:
if sleepCounter/int(app.config.scrapeInterval) == 0 {
break
}
log.Print("Fetching servers")
sleepCounter = 0
servers, err := client.SbServer.List(context.Background())
if err != nil {
log.Println(err)
break
}
log.Printf("found %d servers", len(servers))
app.collector.Data = servers
}
sleepCounter++
time.Sleep(time.Second)
}
}
func (app *application) webListener() {
log.Printf("listening on %s", app.config.metricsAddress)
http.Handle("/", http.RedirectHandler("/metrics", http.StatusPermanentRedirect))
http.Handle("/metrics", promhttp.Handler())
if err := app.server.ListenAndServe(); err != nil {
log.Println(err)
}
}
var app = application{}
type appSettings struct {
metricsAddress string
exitOnRequestError bool
scrapeInterval int
}
func init() {
var settings appSettings
config := kingpin.New(os.Args[0], "sb-server-monitor")
config.Flag("web.listen-address", "Address to use for the metrics server").Envar("SBSERVERMONITOR_WEB_LISTEN_ADDRESS").Default(":9192").StringVar(&settings.metricsAddress)
config.Flag("collector.scrape-interval", "Interval for the data collection from the Hetzner Server Auction API (in seconds)").Envar("SBSERVERMONITOR_SCRAPE_INTERVAL").Default("30").IntVar(&settings.scrapeInterval)
config.Flag("collector.exit-on-request-error", "When set to true the exporter will immediately exit on a request error").Envar("SBSERVERMONITOR_EXIT_ON_REQUEST_ERROR").Default("false").BoolVar(&settings.exitOnRequestError)
kingpin.MustParse(config.Parse(os.Args[1:]))
app.config = &settings
app.collector = sbservermonitor.NewExporter(app.config.exitOnRequestError)
prometheus.MustRegister(app.collector)
app.server = &http.Server{
Handler: nil,
Addr: app.config.metricsAddress,
}
}
func main() {
signals := make(chan os.Signal, 1)
signal.Notify(signals, syscall.SIGTERM)
signal.Notify(signals, syscall.SIGINT)
ctx, cancelWorkers := context.WithCancel(context.Background())
done := make(chan interface{})
go app.webListener()
log.Printf("start worker")
go app.refreshWorker(ctx, done)
<-signals
cancelWorkers()
if err := app.server.Shutdown(context.Background()); err != nil {
log.Println(err)
}
log.Println("sb-server-monitor exporter stopped")
}