metrics: remove deleted peer series (#748)

This commit is contained in:
axlrod
2026-09-01 22:28:10 +02:00
committed by GitHub
parent 87aef1c8ff
commit 3690ff3baa
3 changed files with 101 additions and 0 deletions
+18
View File
@@ -136,3 +136,21 @@ func (m *MetricsServer) UpdatePeerMetrics(peer *domain.Peer, status domain.PeerS
m.peerSendBytesTotal.WithLabelValues(labels...).Set(float64(status.BytesTransmitted)) m.peerSendBytesTotal.WithLabelValues(labels...).Set(float64(status.BytesTransmitted))
m.peerIsConnected.WithLabelValues(labels...).Set(internal.BoolToFloat64(status.IsConnected)) m.peerIsConnected.WithLabelValues(labels...).Set(internal.BoolToFloat64(status.IsConnected))
} }
// DeletePeerMetrics removes all Prometheus series for a peer that was deleted
// from the WireGuard portal. GaugeVecs retain label values until explicitly
// deleted, so leaving these behind makes a deleted peer indistinguishable from
// a retained peer that is currently offline.
func (m *MetricsServer) DeletePeerMetrics(peer domain.Peer) {
labels := []string{
string(peer.InterfaceIdentifier),
peer.Interface.AddressStr(),
string(peer.Identifier),
peer.DisplayName,
string(peer.UserIdentifier),
}
m.peerLastHandshakeSeconds.DeleteLabelValues(labels...)
m.peerReceivedBytesTotal.DeleteLabelValues(labels...)
m.peerSendBytesTotal.DeleteLabelValues(labels...)
m.peerIsConnected.DeleteLabelValues(labels...)
}
+77
View File
@@ -0,0 +1,77 @@
package adapters
import (
"testing"
"github.com/h44z/wg-portal/internal/domain"
"github.com/prometheus/client_golang/prometheus"
)
func TestDeletePeerMetricsRemovesAllPeerSeries(t *testing.T) {
labels := []string{"interface", "addresses", "id", "name", "user"}
m := &MetricsServer{
peerIsConnected: prometheus.NewGaugeVec(prometheus.GaugeOpts{Name: "peer_up_test"}, labels),
peerLastHandshakeSeconds: prometheus.NewGaugeVec(prometheus.GaugeOpts{Name: "peer_handshake_test"}, labels),
peerReceivedBytesTotal: prometheus.NewGaugeVec(prometheus.GaugeOpts{Name: "peer_received_test"}, labels),
peerSendBytesTotal: prometheus.NewGaugeVec(prometheus.GaugeOpts{Name: "peer_sent_test"}, labels),
}
addr, err := domain.CidrFromString("10.0.0.2/32")
if err != nil {
t.Fatal(err)
}
peer := &domain.Peer{
Identifier: "peer-id",
InterfaceIdentifier: "wg0",
DisplayName: "peer",
UserIdentifier: "user",
Interface: domain.PeerInterfaceConfig{Addresses: []domain.Cidr{addr}},
}
m.UpdatePeerMetrics(peer, domain.PeerStatus{PeerId: peer.Identifier, IsConnected: true})
m.DeletePeerMetrics(*peer)
for name, collector := range map[string]prometheus.Collector{
"up": m.peerIsConnected, "handshake": m.peerLastHandshakeSeconds,
"received": m.peerReceivedBytesTotal, "sent": m.peerSendBytesTotal,
} {
ch := make(chan prometheus.Metric, 1)
collector.Collect(ch)
close(ch)
if _, ok := <-ch; ok {
t.Errorf("%s metric remained after peer deletion", name)
}
}
}
func TestDeletePeerMetricsLeavesRetainedPeerSeries(t *testing.T) {
labels := []string{"interface", "addresses", "id", "name", "user"}
m := &MetricsServer{
peerIsConnected: prometheus.NewGaugeVec(prometheus.GaugeOpts{Name: "peer_up_retained_test"}, labels),
peerLastHandshakeSeconds: prometheus.NewGaugeVec(prometheus.GaugeOpts{Name: "peer_handshake_retained_test"}, labels),
peerReceivedBytesTotal: prometheus.NewGaugeVec(prometheus.GaugeOpts{Name: "peer_received_retained_test"}, labels),
peerSendBytesTotal: prometheus.NewGaugeVec(prometheus.GaugeOpts{Name: "peer_sent_retained_test"}, labels),
}
deletedAddr, err := domain.CidrFromString("10.0.0.2/32")
if err != nil {
t.Fatal(err)
}
retainedAddr, err := domain.CidrFromString("10.0.0.3/32")
if err != nil {
t.Fatal(err)
}
deleted := &domain.Peer{Identifier: "deleted", InterfaceIdentifier: "wg0", DisplayName: "deleted", UserIdentifier: "user", Interface: domain.PeerInterfaceConfig{Addresses: []domain.Cidr{deletedAddr}}}
retained := &domain.Peer{Identifier: "retained", InterfaceIdentifier: "wg0", DisplayName: "retained", UserIdentifier: "user", Interface: domain.PeerInterfaceConfig{Addresses: []domain.Cidr{retainedAddr}}}
m.UpdatePeerMetrics(deleted, domain.PeerStatus{PeerId: deleted.Identifier, IsConnected: false})
m.UpdatePeerMetrics(retained, domain.PeerStatus{PeerId: retained.Identifier, IsConnected: false})
m.DeletePeerMetrics(*deleted)
ch := make(chan prometheus.Metric, 2)
m.peerIsConnected.Collect(ch)
close(ch)
count := 0
for range ch {
count++
}
if count != 1 {
t.Fatalf("expected one retained peer series, got %d", count)
}
}
+6
View File
@@ -31,6 +31,7 @@ type StatisticsDatabaseRepo interface {
type StatisticsMetricsServer interface { type StatisticsMetricsServer interface {
UpdateInterfaceMetrics(status domain.InterfaceStatus) UpdateInterfaceMetrics(status domain.InterfaceStatus)
UpdatePeerMetrics(peer *domain.Peer, status domain.PeerStatus) UpdatePeerMetrics(peer *domain.Peer, status domain.PeerStatus)
DeletePeerMetrics(peer domain.Peer)
} }
type StatisticsEventBus interface { type StatisticsEventBus interface {
@@ -433,6 +434,11 @@ func (c *StatisticsCollector) updatePeerMetrics(ctx context.Context, status doma
func (c *StatisticsCollector) connectToMessageBus() { func (c *StatisticsCollector) connectToMessageBus() {
_ = c.bus.Subscribe(app.TopicPeerIdentifierUpdated, c.handlePeerIdentifierChangeEvent) _ = c.bus.Subscribe(app.TopicPeerIdentifierUpdated, c.handlePeerIdentifierChangeEvent)
_ = c.bus.Subscribe(app.TopicPeerDeleted, c.handlePeerDeletedEvent)
}
func (c *StatisticsCollector) handlePeerDeletedEvent(peer domain.Peer) {
c.ms.DeletePeerMetrics(peer)
} }
func (c *StatisticsCollector) handlePeerIdentifierChangeEvent(oldIdentifier, newIdentifier domain.PeerIdentifier) { func (c *StatisticsCollector) handlePeerIdentifierChangeEvent(oldIdentifier, newIdentifier domain.PeerIdentifier) {