Skip to content

Commit 90683bd

Browse files
committed
refactor(go.d): migrate WireGuard collector to framework v2
Move the wireguard collector to collectorapi.CollectorV2 with a metrix store and an embedded charts.yaml, replacing the V1 map output and the hand-built charts. Device and peer charts are now chart instances keyed by the device and public_key labels, so a device or peer that goes away expires through the chart lifecycle. The V1 cleanup removed charts by chart-ID prefix, which also removed the charts of every device whose name started with the removed one (removing wg1 took wg10 with it). Contexts, titles, families, units, chart types, priorities, dimension names, algorithms, multipliers and labels are unchanged. A test compares them and the collected values against a manifest rendered by the V1 collector. Chart IDs keep their V1 base, but the instance suffix now follows it (device_wg0_network_io becomes device_network_io_wg0).
1 parent ca99f1b commit 90683bd

6 files changed

Lines changed: 835 additions & 506 deletions

File tree

‎src/go/plugin/go.d/collector/wireguard/charts.go‎

Lines changed: 0 additions & 152 deletions
This file was deleted.
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
version: v1
2+
context_namespace: wireguard
3+
groups:
4+
- family: device traffic
5+
metrics:
6+
- device_receive
7+
- device_transmit
8+
chart_defaults:
9+
priority: 70000
10+
charts:
11+
- id: device_network_io
12+
title: Device traffic
13+
context: device_network_io
14+
units: B/s
15+
type: area
16+
instances:
17+
by_labels: [device]
18+
dimensions:
19+
- selector: device_receive
20+
name: receive
21+
- selector: device_transmit
22+
name: transmit
23+
options:
24+
multiplier: -1
25+
- family: device peers
26+
metrics:
27+
- device_peers
28+
chart_defaults:
29+
priority: 70001
30+
charts:
31+
- id: device_peers
32+
title: Device peers
33+
context: device_peers
34+
units: peers
35+
instances:
36+
by_labels: [device]
37+
dimensions:
38+
- selector: device_peers
39+
name: peers
40+
- family: peer traffic
41+
metrics:
42+
- peer_receive
43+
- peer_transmit
44+
chart_defaults:
45+
priority: 70002
46+
charts:
47+
- id: peer_network_io
48+
title: Peer traffic
49+
context: peer_network_io
50+
units: B/s
51+
type: area
52+
instances:
53+
by_labels: [device, public_key]
54+
dimensions:
55+
- selector: peer_receive
56+
name: receive
57+
- selector: peer_transmit
58+
name: transmit
59+
options:
60+
multiplier: -1
61+
- family: peer latest handshake
62+
metrics:
63+
- peer_latest_handshake_ago
64+
chart_defaults:
65+
priority: 70003
66+
charts:
67+
- id: peer_latest_handshake_ago
68+
title: Peer time elapsed since the latest handshake
69+
context: peer_latest_handshake_ago
70+
units: seconds
71+
instances:
72+
by_labels: [device, public_key]
73+
dimensions:
74+
- selector: peer_latest_handshake_ago
75+
name: time

‎src/go/plugin/go.d/collector/wireguard/collect.go‎

Lines changed: 44 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,22 @@ import (
99
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
1010
)
1111

12-
func (c *Collector) collect() (map[string]int64, error) {
12+
func (c *Collector) collect() error {
13+
devices, err := c.devices()
14+
if err != nil {
15+
return err
16+
}
17+
18+
if len(devices) == 0 {
19+
c.Info("no WireGuard devices found on the host system")
20+
}
21+
22+
c.writeMetrics(devices, c.now())
23+
24+
return nil
25+
}
26+
27+
func (c *Collector) devices() ([]*wgtypes.Device, error) {
1328
if c.client == nil {
1429
client, err := c.newWGClient()
1530
if err != nil {
@@ -25,85 +40,49 @@ func (c *Collector) collect() (map[string]int64, error) {
2540
return nil, fmt.Errorf("retrieving WireGuard devices: %v", err)
2641
}
2742

28-
if len(devices) == 0 {
29-
c.Info("no WireGuard devices found on the host system")
30-
}
31-
32-
now := time.Now()
33-
if c.cleanupLastTime.IsZero() {
34-
c.cleanupLastTime = now
35-
}
36-
37-
mx := make(map[string]int64)
38-
39-
c.collectDevicesPeers(mx, devices, now)
43+
return devices, nil
44+
}
4045

41-
if now.Sub(c.cleanupLastTime) > c.cleanupEvery {
42-
c.cleanupLastTime = now
43-
c.cleanupDevicesPeers(devices)
44-
}
46+
// writeMetrics builds the instruments every cycle: devices and peers come and go with the host's WireGuard
47+
// configuration, so a long-lived Vec would keep a handle for every peer ever seen.
48+
func (c *Collector) writeMetrics(devices []*wgtypes.Device, now time.Time) {
49+
meter := c.store.Write().SnapshotMeter("")
50+
deviceVec := meter.Vec("device")
51+
peerVec := meter.Vec("device", "public_key")
4552

46-
return mx, nil
47-
}
53+
devicePeers := deviceVec.Gauge("device_peers")
54+
deviceReceive := deviceVec.Counter("device_receive")
55+
deviceTransmit := deviceVec.Counter("device_transmit")
56+
peerReceive := peerVec.Counter("peer_receive")
57+
peerTransmit := peerVec.Counter("peer_transmit")
58+
peerLatestHandshakeAgo := peerVec.Gauge("peer_latest_handshake_ago")
4859

49-
func (c *Collector) collectDevicesPeers(mx map[string]int64, devices []*wgtypes.Device, now time.Time) {
5060
for _, d := range devices {
51-
if !c.devices[d.Name] {
52-
c.devices[d.Name] = true
53-
c.addNewDeviceCharts(d.Name)
54-
}
61+
devicePeers.WithLabelValues(d.Name).Observe(float64(len(d.Peers)))
5562

56-
mx["device_"+d.Name+"_peers"] = int64(len(d.Peers))
57-
if len(d.Peers) == 0 {
58-
mx["device_"+d.Name+"_receive"] = 0
59-
mx["device_"+d.Name+"_transmit"] = 0
60-
continue
61-
}
63+
// Device traffic is the sum over peers that have completed a handshake. A device without peers reports
64+
// zero; a device whose peers have no handshake yet reports no traffic sample.
65+
var receive, transmit int64
66+
hasTraffic := len(d.Peers) == 0
6267

6368
for _, p := range d.Peers {
6469
if p.LastHandshakeTime.IsZero() {
6570
continue
6671
}
72+
hasTraffic = true
6773

6874
pubKey := p.PublicKey.String()
69-
id := peerID(d.Name, pubKey)
75+
receive += p.ReceiveBytes
76+
transmit += p.TransmitBytes
7077

71-
if !c.peers[id] {
72-
c.peers[id] = true
73-
c.addNewPeerCharts(id, d.Name, pubKey)
74-
}
75-
76-
mx["device_"+d.Name+"_receive"] += p.ReceiveBytes
77-
mx["device_"+d.Name+"_transmit"] += p.TransmitBytes
78-
mx["peer_"+id+"_receive"] = p.ReceiveBytes
79-
mx["peer_"+id+"_transmit"] = p.TransmitBytes
80-
mx["peer_"+id+"_latest_handshake_ago"] = int64(now.Sub(p.LastHandshakeTime).Seconds())
78+
peerReceive.WithLabelValues(d.Name, pubKey).ObserveTotal(float64(p.ReceiveBytes))
79+
peerTransmit.WithLabelValues(d.Name, pubKey).ObserveTotal(float64(p.TransmitBytes))
80+
peerLatestHandshakeAgo.WithLabelValues(d.Name, pubKey).Observe(float64(int64(now.Sub(p.LastHandshakeTime).Seconds())))
8181
}
82-
}
83-
}
8482

85-
func (c *Collector) cleanupDevicesPeers(devices []*wgtypes.Device) {
86-
seenDevices, seenPeers := make(map[string]bool), make(map[string]bool)
87-
for _, d := range devices {
88-
seenDevices[d.Name] = true
89-
for _, p := range d.Peers {
90-
seenPeers[peerID(d.Name, p.PublicKey.String())] = true
91-
}
92-
}
93-
for d := range c.devices {
94-
if !seenDevices[d] {
95-
delete(c.devices, d)
96-
c.removeDeviceCharts(d)
83+
if hasTraffic {
84+
deviceReceive.WithLabelValues(d.Name).ObserveTotal(float64(receive))
85+
deviceTransmit.WithLabelValues(d.Name).ObserveTotal(float64(transmit))
9786
}
9887
}
99-
for p := range c.peers {
100-
if !seenPeers[p] {
101-
delete(c.peers, p)
102-
c.removePeerCharts(p)
103-
}
104-
}
105-
}
106-
107-
func peerID(device, peerPublicKey string) string {
108-
return device + "_" + peerPublicKey
10988
}

0 commit comments

Comments
 (0)