Files
pve-exporter/metrics/pve_metrics_naming_test.go
lostakj 92e54418bc
Build Docker image on push / docker (push) Successful in 21s
Build and push Docker image on tag / docker (push) Successful in 22s
Align metric names with Prometheus naming conventions
Use base units, export cumulative values as _total counters, update docs, dashboards and rules.
2026-08-24 19:13:05 +02:00

73 lines
2.5 KiB
Go

package metrics
import (
"testing"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/testutil/promlint"
)
// Verifies that all exported PVE metrics follow the Prometheus naming conventions
// (base units, '_total' suffix on counters, '_info' suffix on info metrics, ...)
// so that tools such as promtool or Grafana do not report naming problems.
func TestPveMetricNamesFollowPrometheusConventions(t *testing.T) {
registry := prometheus.NewRegistry()
// Collectors register their metrics on the default registerer, so it is
// temporarily replaced by a dedicated registry.
defaultRegisterer := prometheus.DefaultRegisterer
prometheus.DefaultRegisterer = registry
defer func() { prometheus.DefaultRegisterer = defaultRegisterer }()
// Collector constructors only create metrics, the API client is used during
// collection only, so a nil client is enough here.
ttlRegistry := NewTTLRegistry()
NewPveClusterStateCollector(nil, ttlRegistry)
NewPveNodeStatusCollector(nil, ttlRegistry)
NewPveSubscriptionCollector(nil, ttlRegistry)
NewPveNodeDiskCollector(nil, ttlRegistry)
NewPveNodeZfsCollector(nil, ttlRegistry)
NewPveSdnCollector(nil, ttlRegistry)
NewPveStorageCollector(nil, ttlRegistry)
NewPveContainerCollector(nil, ttlRegistry)
NewPveVirtualMachineCollector(nil, ttlRegistry)
// Metric vectors are exported only once they hold a label set.
for _, metric := range ttlRegistry.metrics {
switch m := metric.(type) {
case *TTLGaugeVec:
m.With(emptyLabels(m.labelNames)).Set(0)
case *TTLCounterVec:
m.With(emptyLabels(m.labelNames)).Set(0)
default:
t.Fatalf("Unknown TTL metric type %T.", metric)
}
}
families, err := registry.Gather()
if err != nil {
t.Fatalf("Unable to gather metrics. Error: %s.", err)
}
if len(families) != len(ttlRegistry.metrics) {
t.Fatalf("Gathered %d metric families but %d metrics are registered.", len(families), len(ttlRegistry.metrics))
}
problems, err := promlint.NewWithMetricFamilies(families).Lint()
if err != nil {
t.Fatalf("Unable to lint metrics. Error: %s.", err)
}
for _, problem := range problems {
t.Errorf("Metric '%s' violates the Prometheus naming conventions: %s.", problem.Metric, problem.Text)
}
}
// emptyLabels builds a label set with all given label names set to an empty value.
func emptyLabels(labelNames []string) prometheus.Labels {
labels := prometheus.Labels{}
for _, name := range labelNames {
labels[name] = ""
}
return labels
}