--provider-check-interval (1m; 0 = off, else at least 10s) and
--provider-check-timeout (15s) are validated before the providers config
loads. Checkers are collected from the unwrapped providers in config
order, so checks never count as provider requests. Three scrape-time
gauges per {provider,target} read the monitor's snapshot: up, check
duration and check timestamp. The e2e suite waits for the kubernetes
ProviderHealth to turn Ready and asserts its gauge in the scrape.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
156 lines
5.3 KiB
Go
156 lines
5.3 KiB
Go
package metrics
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
"github.com/prometheus/client_golang/prometheus/testutil"
|
|
)
|
|
|
|
// register wires a fresh registry — the reason Register exists instead of
|
|
// init()-time self-registration.
|
|
func register(t *testing.T, m *Metrics, phases map[string]int, active int) *prometheus.Registry {
|
|
t.Helper()
|
|
reg := prometheus.NewRegistry()
|
|
err := m.Register(reg,
|
|
func() map[string]int { return phases },
|
|
func() int { return active },
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("Register: %v", err)
|
|
}
|
|
return reg
|
|
}
|
|
|
|
func TestRegister_scrapeTimeCollectors(t *testing.T) {
|
|
t.Parallel()
|
|
m := New()
|
|
reg := register(t, m, map[string]int{"Ready": 3, "Provisioning": 1}, 7)
|
|
|
|
expected := `
|
|
# HELP proxy_operator_leases_active Currently active leases.
|
|
# TYPE proxy_operator_leases_active gauge
|
|
proxy_operator_leases_active 7
|
|
# HELP proxy_operator_proxies Proxy objects by phase.
|
|
# TYPE proxy_operator_proxies gauge
|
|
proxy_operator_proxies{phase="Provisioning"} 1
|
|
proxy_operator_proxies{phase="Ready"} 3
|
|
`
|
|
if err := testutil.GatherAndCompare(reg, strings.NewReader(expected),
|
|
"proxy_operator_proxies", "proxy_operator_leases_active"); err != nil {
|
|
t.Error(err)
|
|
}
|
|
}
|
|
|
|
func TestObserveProbe_andForget(t *testing.T) {
|
|
t.Parallel()
|
|
m := New()
|
|
reg := register(t, m, nil, 0)
|
|
|
|
m.ObserveProbe("default/p1", 30*time.Millisecond, true)
|
|
m.ObserveProbe("default/p1", 40*time.Millisecond, false)
|
|
m.ObserveProbe("default/p2", 10*time.Millisecond, true)
|
|
|
|
if got := testutil.CollectAndCount(m.healthcheckDuration); got != 2 {
|
|
t.Errorf("duration series = %d, want 2 (one per proxy)", got)
|
|
}
|
|
if got := testutil.ToFloat64(m.healthcheckFailures.WithLabelValues("default/p1")); got != 1 {
|
|
t.Errorf("p1 failures = %v, want 1 (only the failed probe)", got)
|
|
}
|
|
|
|
m.ForgetProxy("default/p1")
|
|
if got := testutil.CollectAndCount(m.healthcheckDuration); got != 1 {
|
|
t.Errorf("duration series after ForgetProxy = %d, want 1 — series must not leak", got)
|
|
}
|
|
if got := testutil.CollectAndCount(m.healthcheckFailures); got != 0 {
|
|
t.Errorf("failure series after ForgetProxy = %d, want 0", got)
|
|
}
|
|
_ = reg
|
|
}
|
|
|
|
func TestLeaseRequest(t *testing.T) {
|
|
t.Parallel()
|
|
m := New()
|
|
register(t, m, nil, 0)
|
|
|
|
m.LeaseRequest("granted")
|
|
m.LeaseRequest("granted")
|
|
m.LeaseRequest("no_match")
|
|
|
|
if got := testutil.ToFloat64(m.leaseRequests.WithLabelValues("granted")); got != 2 {
|
|
t.Errorf("granted = %v, want 2", got)
|
|
}
|
|
if got := testutil.ToFloat64(m.leaseRequests.WithLabelValues("no_match")); got != 1 {
|
|
t.Errorf("no_match = %v, want 1", got)
|
|
}
|
|
}
|
|
|
|
func TestProviderRequest(t *testing.T) {
|
|
t.Parallel()
|
|
m := New()
|
|
register(t, m, nil, 0)
|
|
|
|
m.ProviderRequest("gcp-eu", "create", "ok")
|
|
m.ProviderRequest("gcp-eu", "create", "quota_exceeded")
|
|
|
|
if got := testutil.ToFloat64(m.providerRequests.WithLabelValues("gcp-eu", "create", "ok")); got != 1 {
|
|
t.Errorf("ok = %v, want 1", got)
|
|
}
|
|
if got := testutil.ToFloat64(m.providerRequests.WithLabelValues("gcp-eu", "create", "quota_exceeded")); got != 1 {
|
|
t.Errorf("quota_exceeded = %v, want 1", got)
|
|
}
|
|
}
|
|
|
|
func TestRegister_freshRegistryPerTest(t *testing.T) {
|
|
t.Parallel()
|
|
// Registering the same metric set on two registries must both succeed —
|
|
// the property init()-style global registration would break.
|
|
m1, m2 := New(), New()
|
|
register(t, m1, nil, 0)
|
|
register(t, m2, nil, 0)
|
|
}
|
|
|
|
func TestRegisterProviderTargets_scrape(t *testing.T) {
|
|
t.Parallel()
|
|
checked := time.Unix(1757602800, 0)
|
|
targets := []ProviderTarget{
|
|
{Provider: "kvm-home", Target: "nas1", Up: true, Duration: 84 * time.Millisecond, CheckedAt: checked},
|
|
{Provider: "kvm-home", Target: "nas2", Up: false, Duration: 15 * time.Second, CheckedAt: checked},
|
|
}
|
|
reg := prometheus.NewRegistry()
|
|
if err := RegisterProviderTargets(reg, func() []ProviderTarget { return targets }); err != nil {
|
|
t.Fatalf("RegisterProviderTargets: %v", err)
|
|
}
|
|
|
|
expected := `
|
|
# HELP proxy_operator_provider_check_duration_seconds Duration of the target's latest provider health check.
|
|
# TYPE proxy_operator_provider_check_duration_seconds gauge
|
|
proxy_operator_provider_check_duration_seconds{provider="kvm-home",target="nas1"} 0.084
|
|
proxy_operator_provider_check_duration_seconds{provider="kvm-home",target="nas2"} 15
|
|
# HELP proxy_operator_provider_check_timestamp_seconds Unix time of the target's latest provider health check.
|
|
# TYPE proxy_operator_provider_check_timestamp_seconds gauge
|
|
proxy_operator_provider_check_timestamp_seconds{provider="kvm-home",target="nas1"} 1.7576028e+09
|
|
proxy_operator_provider_check_timestamp_seconds{provider="kvm-home",target="nas2"} 1.7576028e+09
|
|
# HELP proxy_operator_provider_target_up Whether the target passed its latest provider health check (1) or not (0).
|
|
# TYPE proxy_operator_provider_target_up gauge
|
|
proxy_operator_provider_target_up{provider="kvm-home",target="nas1"} 1
|
|
proxy_operator_provider_target_up{provider="kvm-home",target="nas2"} 0
|
|
`
|
|
if err := testutil.GatherAndCompare(reg, strings.NewReader(expected)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
}
|
|
|
|
func TestRegisterProviderTargets_emptySnapshotHasNoSeries(t *testing.T) {
|
|
t.Parallel()
|
|
reg := prometheus.NewRegistry()
|
|
if err := RegisterProviderTargets(reg, func() []ProviderTarget { return nil }); err != nil {
|
|
t.Fatalf("RegisterProviderTargets: %v", err)
|
|
}
|
|
if n, err := testutil.GatherAndCount(reg); err != nil || n != 0 {
|
|
t.Errorf("series before the first check = %d (%v), want 0", n, err)
|
|
}
|
|
}
|