aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWei He <weihe924stephen@gmail.com>2020-06-02 17:52:00 +0900
committerGitHub <noreply@github.com>2020-06-02 10:52:00 +0200
commit02532771213fcb024608043d7b1e514c1a80c2da (patch)
tree36e8a19a2f311deafca09763ea90a1cd3a260678
parentb7cb72adeb6b223dda8c0eb4bd2a16421b202d8e (diff)
downloadprometheus_node_collector-02532771213fcb024608043d7b1e514c1a80c2da.tar.bz2
prometheus_node_collector-02532771213fcb024608043d7b1e514c1a80c2da.tar.xz
prometheus_node_collector-02532771213fcb024608043d7b1e514c1a80c2da.zip
Add flag to aggr ipvs metrics to avoid high cardinality metrics (#1709)
Fixes #1708 Signed-off-by: Wing924 <weihe924stephen@gmail.com>
-rw-r--r--collector/fixtures/ip_vs_result_lbs_local_address_local_port.txt33
-rw-r--r--collector/fixtures/ip_vs_result_lbs_local_port.txt27
-rw-r--r--collector/fixtures/ip_vs_result_lbs_none.txt24
-rw-r--r--collector/ipvs_linux.go121
-rw-r--r--collector/ipvs_linux_test.go221
5 files changed, 345 insertions, 81 deletions
diff --git a/collector/fixtures/ip_vs_result_lbs_local_address_local_port.txt b/collector/fixtures/ip_vs_result_lbs_local_address_local_port.txt
new file mode 100644
index 0000000..61c77c7
--- /dev/null
+++ b/collector/fixtures/ip_vs_result_lbs_local_address_local_port.txt
@@ -0,0 +1,33 @@
1# HELP node_ipvs_backend_connections_active The current active connections by local and remote address.
2# TYPE node_ipvs_backend_connections_active gauge
3node_ipvs_backend_connections_active{local_address="",local_port="0"} 385
4node_ipvs_backend_connections_active{local_address="192.168.0.22",local_port="3306"} 744
5node_ipvs_backend_connections_active{local_address="192.168.0.55",local_port="3306"} 0
6node_ipvs_backend_connections_active{local_address="192.168.0.57",local_port="3306"} 2997
7# HELP node_ipvs_backend_connections_inactive The current inactive connections by local and remote address.
8# TYPE node_ipvs_backend_connections_inactive gauge
9node_ipvs_backend_connections_inactive{local_address="",local_port="0"} 6
10node_ipvs_backend_connections_inactive{local_address="192.168.0.22",local_port="3306"} 5
11node_ipvs_backend_connections_inactive{local_address="192.168.0.55",local_port="3306"} 0
12node_ipvs_backend_connections_inactive{local_address="192.168.0.57",local_port="3306"} 0
13# HELP node_ipvs_backend_weight The current backend weight by local and remote address.
14# TYPE node_ipvs_backend_weight gauge
15node_ipvs_backend_weight{local_address="",local_port="0"} 120
16node_ipvs_backend_weight{local_address="192.168.0.22",local_port="3306"} 300
17node_ipvs_backend_weight{local_address="192.168.0.55",local_port="3306"} 100
18node_ipvs_backend_weight{local_address="192.168.0.57",local_port="3306"} 200
19# HELP node_ipvs_connections_total The total number of connections made.
20# TYPE node_ipvs_connections_total counter
21node_ipvs_connections_total 2.3765872e+07
22# HELP node_ipvs_incoming_bytes_total The total amount of incoming data.
23# TYPE node_ipvs_incoming_bytes_total counter
24node_ipvs_incoming_bytes_total 8.9991519156915e+13
25# HELP node_ipvs_incoming_packets_total The total number of incoming packets.
26# TYPE node_ipvs_incoming_packets_total counter
27node_ipvs_incoming_packets_total 3.811989221e+09
28# HELP node_ipvs_outgoing_bytes_total The total amount of outgoing data.
29# TYPE node_ipvs_outgoing_bytes_total counter
30node_ipvs_outgoing_bytes_total 0
31# HELP node_ipvs_outgoing_packets_total The total number of outgoing packets.
32# TYPE node_ipvs_outgoing_packets_total counter
33node_ipvs_outgoing_packets_total 0
diff --git a/collector/fixtures/ip_vs_result_lbs_local_port.txt b/collector/fixtures/ip_vs_result_lbs_local_port.txt
new file mode 100644
index 0000000..ef931ff
--- /dev/null
+++ b/collector/fixtures/ip_vs_result_lbs_local_port.txt
@@ -0,0 +1,27 @@
1# HELP node_ipvs_backend_connections_active The current active connections by local and remote address.
2# TYPE node_ipvs_backend_connections_active gauge
3node_ipvs_backend_connections_active{local_port="0"} 385
4node_ipvs_backend_connections_active{local_port="3306"} 3741
5# HELP node_ipvs_backend_connections_inactive The current inactive connections by local and remote address.
6# TYPE node_ipvs_backend_connections_inactive gauge
7node_ipvs_backend_connections_inactive{local_port="0"} 6
8node_ipvs_backend_connections_inactive{local_port="3306"} 5
9# HELP node_ipvs_backend_weight The current backend weight by local and remote address.
10# TYPE node_ipvs_backend_weight gauge
11node_ipvs_backend_weight{local_port="0"} 120
12node_ipvs_backend_weight{local_port="3306"} 600
13# HELP node_ipvs_connections_total The total number of connections made.
14# TYPE node_ipvs_connections_total counter
15node_ipvs_connections_total 2.3765872e+07
16# HELP node_ipvs_incoming_bytes_total The total amount of incoming data.
17# TYPE node_ipvs_incoming_bytes_total counter
18node_ipvs_incoming_bytes_total 8.9991519156915e+13
19# HELP node_ipvs_incoming_packets_total The total number of incoming packets.
20# TYPE node_ipvs_incoming_packets_total counter
21node_ipvs_incoming_packets_total 3.811989221e+09
22# HELP node_ipvs_outgoing_bytes_total The total amount of outgoing data.
23# TYPE node_ipvs_outgoing_bytes_total counter
24node_ipvs_outgoing_bytes_total 0
25# HELP node_ipvs_outgoing_packets_total The total number of outgoing packets.
26# TYPE node_ipvs_outgoing_packets_total counter
27node_ipvs_outgoing_packets_total 0
diff --git a/collector/fixtures/ip_vs_result_lbs_none.txt b/collector/fixtures/ip_vs_result_lbs_none.txt
new file mode 100644
index 0000000..621aa54
--- /dev/null
+++ b/collector/fixtures/ip_vs_result_lbs_none.txt
@@ -0,0 +1,24 @@
1# HELP node_ipvs_backend_connections_active The current active connections by local and remote address.
2# TYPE node_ipvs_backend_connections_active gauge
3node_ipvs_backend_connections_active 4126
4# HELP node_ipvs_backend_connections_inactive The current inactive connections by local and remote address.
5# TYPE node_ipvs_backend_connections_inactive gauge
6node_ipvs_backend_connections_inactive 11
7# HELP node_ipvs_backend_weight The current backend weight by local and remote address.
8# TYPE node_ipvs_backend_weight gauge
9node_ipvs_backend_weight 720
10# HELP node_ipvs_connections_total The total number of connections made.
11# TYPE node_ipvs_connections_total counter
12node_ipvs_connections_total 2.3765872e+07
13# HELP node_ipvs_incoming_bytes_total The total amount of incoming data.
14# TYPE node_ipvs_incoming_bytes_total counter
15node_ipvs_incoming_bytes_total 8.9991519156915e+13
16# HELP node_ipvs_incoming_packets_total The total number of incoming packets.
17# TYPE node_ipvs_incoming_packets_total counter
18node_ipvs_incoming_packets_total 3.811989221e+09
19# HELP node_ipvs_outgoing_bytes_total The total amount of outgoing data.
20# TYPE node_ipvs_outgoing_bytes_total counter
21node_ipvs_outgoing_bytes_total 0
22# HELP node_ipvs_outgoing_packets_total The total number of outgoing packets.
23# TYPE node_ipvs_outgoing_packets_total counter
24node_ipvs_outgoing_packets_total 0
diff --git a/collector/ipvs_linux.go b/collector/ipvs_linux.go
index 11adf0f..b5c8d73 100644
--- a/collector/ipvs_linux.go
+++ b/collector/ipvs_linux.go
@@ -18,22 +18,53 @@ package collector
18import ( 18import (
19 "fmt" 19 "fmt"
20 "os" 20 "os"
21 "sort"
21 "strconv" 22 "strconv"
23 "strings"
22 24
23 "github.com/go-kit/kit/log" 25 "github.com/go-kit/kit/log"
24 "github.com/go-kit/kit/log/level" 26 "github.com/go-kit/kit/log/level"
25 "github.com/prometheus/client_golang/prometheus" 27 "github.com/prometheus/client_golang/prometheus"
26 "github.com/prometheus/procfs" 28 "github.com/prometheus/procfs"
29 kingpin "gopkg.in/alecthomas/kingpin.v2"
27) 30)
28 31
29type ipvsCollector struct { 32type ipvsCollector struct {
30 Collector 33 Collector
31 fs procfs.FS 34 fs procfs.FS
35 backendLabels []string
32 backendConnectionsActive, backendConnectionsInact, backendWeight typedDesc 36 backendConnectionsActive, backendConnectionsInact, backendWeight typedDesc
33 connections, incomingPackets, outgoingPackets, incomingBytes, outgoingBytes typedDesc 37 connections, incomingPackets, outgoingPackets, incomingBytes, outgoingBytes typedDesc
34 logger log.Logger 38 logger log.Logger
35} 39}
36 40
41type ipvsBackendStatus struct {
42 ActiveConn uint64
43 InactConn uint64
44 Weight uint64
45}
46
47const (
48 ipvsLabelLocalAddress = "local_address"
49 ipvsLabelLocalPort = "local_port"
50 ipvsLabelRemoteAddress = "remote_address"
51 ipvsLabelRemotePort = "remote_port"
52 ipvsLabelProto = "proto"
53 ipvsLabelLocalMark = "local_mark"
54)
55
56var (
57 fullIpvsBackendLabels = []string{
58 ipvsLabelLocalAddress,
59 ipvsLabelLocalPort,
60 ipvsLabelRemoteAddress,
61 ipvsLabelRemotePort,
62 ipvsLabelProto,
63 ipvsLabelLocalMark,
64 }
65 ipvsLabels = kingpin.Flag("collector.ipvs.backend-labels", "Comma separated list for IPVS backend stats labels.").Default(strings.Join(fullIpvsBackendLabels, ",")).String()
66)
67
37func init() { 68func init() {
38 registerCollector("ipvs", defaultEnabled, NewIPVSCollector) 69 registerCollector("ipvs", defaultEnabled, NewIPVSCollector)
39} 70}
@@ -46,19 +77,15 @@ func NewIPVSCollector(logger log.Logger) (Collector, error) {
46 77
47func newIPVSCollector(logger log.Logger) (*ipvsCollector, error) { 78func newIPVSCollector(logger log.Logger) (*ipvsCollector, error) {
48 var ( 79 var (
49 ipvsBackendLabelNames = []string{
50 "local_address",
51 "local_port",
52 "remote_address",
53 "remote_port",
54 "proto",
55 "local_mark",
56 }
57 c ipvsCollector 80 c ipvsCollector
58 err error 81 err error
59 subsystem = "ipvs" 82 subsystem = "ipvs"
60 ) 83 )
61 84
85 if c.backendLabels, err = c.parseIpvsLabels(*ipvsLabels); err != nil {
86 return nil, err
87 }
88
62 c.logger = logger 89 c.logger = logger
63 c.fs, err = procfs.NewFS(*procPath) 90 c.fs, err = procfs.NewFS(*procPath)
64 if err != nil { 91 if err != nil {
@@ -93,17 +120,17 @@ func newIPVSCollector(logger log.Logger) (*ipvsCollector, error) {
93 c.backendConnectionsActive = typedDesc{prometheus.NewDesc( 120 c.backendConnectionsActive = typedDesc{prometheus.NewDesc(
94 prometheus.BuildFQName(namespace, subsystem, "backend_connections_active"), 121 prometheus.BuildFQName(namespace, subsystem, "backend_connections_active"),
95 "The current active connections by local and remote address.", 122 "The current active connections by local and remote address.",
96 ipvsBackendLabelNames, nil, 123 c.backendLabels, nil,
97 ), prometheus.GaugeValue} 124 ), prometheus.GaugeValue}
98 c.backendConnectionsInact = typedDesc{prometheus.NewDesc( 125 c.backendConnectionsInact = typedDesc{prometheus.NewDesc(
99 prometheus.BuildFQName(namespace, subsystem, "backend_connections_inactive"), 126 prometheus.BuildFQName(namespace, subsystem, "backend_connections_inactive"),
100 "The current inactive connections by local and remote address.", 127 "The current inactive connections by local and remote address.",
101 ipvsBackendLabelNames, nil, 128 c.backendLabels, nil,
102 ), prometheus.GaugeValue} 129 ), prometheus.GaugeValue}
103 c.backendWeight = typedDesc{prometheus.NewDesc( 130 c.backendWeight = typedDesc{prometheus.NewDesc(
104 prometheus.BuildFQName(namespace, subsystem, "backend_weight"), 131 prometheus.BuildFQName(namespace, subsystem, "backend_weight"),
105 "The current backend weight by local and remote address.", 132 "The current backend weight by local and remote address.",
106 ipvsBackendLabelNames, nil, 133 c.backendLabels, nil,
107 ), prometheus.GaugeValue} 134 ), prometheus.GaugeValue}
108 135
109 return &c, nil 136 return &c, nil
@@ -130,22 +157,74 @@ func (c *ipvsCollector) Update(ch chan<- prometheus.Metric) error {
130 return fmt.Errorf("could not get backend status: %s", err) 157 return fmt.Errorf("could not get backend status: %s", err)
131 } 158 }
132 159
160 sums := map[string]ipvsBackendStatus{}
161 labelValues := map[string][]string{}
133 for _, backend := range backendStats { 162 for _, backend := range backendStats {
134 localAddress := "" 163 localAddress := ""
135 if backend.LocalAddress.String() != "<nil>" { 164 if backend.LocalAddress.String() != "<nil>" {
136 localAddress = backend.LocalAddress.String() 165 localAddress = backend.LocalAddress.String()
137 } 166 }
138 labelValues := []string{ 167 kv := make([]string, len(c.backendLabels))
139 localAddress, 168 for i, label := range c.backendLabels {
140 strconv.FormatUint(uint64(backend.LocalPort), 10), 169 var labelValue string
141 backend.RemoteAddress.String(), 170 switch label {
142 strconv.FormatUint(uint64(backend.RemotePort), 10), 171 case ipvsLabelLocalAddress:
143 backend.Proto, 172 labelValue = localAddress
144 backend.LocalMark, 173 case ipvsLabelLocalPort:
174 labelValue = strconv.FormatUint(uint64(backend.LocalPort), 10)
175 case ipvsLabelRemoteAddress:
176 labelValue = backend.RemoteAddress.String()
177 case ipvsLabelRemotePort:
178 labelValue = strconv.FormatUint(uint64(backend.RemotePort), 10)
179 case ipvsLabelProto:
180 labelValue = backend.Proto
181 case ipvsLabelLocalMark:
182 labelValue = backend.LocalMark
183 }
184 kv[i] = labelValue
145 } 185 }
146 ch <- c.backendConnectionsActive.mustNewConstMetric(float64(backend.ActiveConn), labelValues...) 186 key := strings.Join(kv, "-")
147 ch <- c.backendConnectionsInact.mustNewConstMetric(float64(backend.InactConn), labelValues...) 187 status := sums[key]
148 ch <- c.backendWeight.mustNewConstMetric(float64(backend.Weight), labelValues...) 188 status.ActiveConn += backend.ActiveConn
189 status.InactConn += backend.InactConn
190 status.Weight += backend.Weight
191 sums[key] = status
192 labelValues[key] = kv
193 }
194 for key, status := range sums {
195 kv := labelValues[key]
196 ch <- c.backendConnectionsActive.mustNewConstMetric(float64(status.ActiveConn), kv...)
197 ch <- c.backendConnectionsInact.mustNewConstMetric(float64(status.InactConn), kv...)
198 ch <- c.backendWeight.mustNewConstMetric(float64(status.Weight), kv...)
149 } 199 }
150 return nil 200 return nil
151} 201}
202
203func (c *ipvsCollector) parseIpvsLabels(labelString string) ([]string, error) {
204 labels := strings.Split(labelString, ",")
205 labelSet := make(map[string]bool, len(labels))
206 results := make([]string, 0, len(labels))
207 for _, label := range labels {
208 if label != "" {
209 labelSet[label] = true
210 }
211 }
212
213 for _, label := range fullIpvsBackendLabels {
214 if labelSet[label] {
215 results = append(results, label)
216 }
217 delete(labelSet, label)
218 }
219
220 if len(labelSet) > 0 {
221 keys := make([]string, 0, len(labelSet))
222 for label := range labelSet {
223 keys = append(keys, label)
224 }
225 sort.Strings(keys)
226 return nil, fmt.Errorf("unknown IPVS backend labels: %q", strings.Join(keys, ", "))
227 }
228
229 return results, nil
230}
diff --git a/collector/ipvs_linux_test.go b/collector/ipvs_linux_test.go
index a1c0cba..43c6839 100644
--- a/collector/ipvs_linux_test.go
+++ b/collector/ipvs_linux_test.go
@@ -14,47 +14,131 @@
14package collector 14package collector
15 15
16import ( 16import (
17 "errors"
17 "fmt" 18 "fmt"
18 "github.com/go-kit/kit/log"
19 "io/ioutil" 19 "io/ioutil"
20 "net/http" 20 "net/http"
21 "net/http/httptest" 21 "net/http/httptest"
22 "strings" 22 "strings"
23 "testing" 23 "testing"
24 24
25 "github.com/go-kit/kit/log"
26
25 "github.com/prometheus/client_golang/prometheus" 27 "github.com/prometheus/client_golang/prometheus"
26 "github.com/prometheus/client_golang/prometheus/promhttp" 28 "github.com/prometheus/client_golang/prometheus/promhttp"
27 "gopkg.in/alecthomas/kingpin.v2" 29 "gopkg.in/alecthomas/kingpin.v2"
28) 30)
29 31
30func TestIPVSCollector(t *testing.T) { 32func TestIPVSCollector(t *testing.T) {
31 if _, err := kingpin.CommandLine.Parse([]string{"--path.procfs", "fixtures/proc"}); err != nil { 33 testcases := []struct {
32 t.Fatal(err) 34 labels string
33 } 35 expects []string
34 collector, err := newIPVSCollector(log.NewNopLogger()) 36 err error
35 if err != nil { 37 }{
36 t.Fatal(err) 38 {
39 "<none>",
40 []string{
41 prometheus.NewDesc("node_ipvs_connections_total", "The total number of connections made.", nil, nil).String(),
42 prometheus.NewDesc("node_ipvs_incoming_packets_total", "The total number of incoming packets.", nil, nil).String(),
43 prometheus.NewDesc("node_ipvs_outgoing_packets_total", "The total number of outgoing packets.", nil, nil).String(),
44 prometheus.NewDesc("node_ipvs_incoming_bytes_total", "The total amount of incoming data.", nil, nil).String(),
45 prometheus.NewDesc("node_ipvs_outgoing_bytes_total", "The total amount of outgoing data.", nil, nil).String(),
46 prometheus.NewDesc("node_ipvs_backend_connections_active", "The current active connections by local and remote address.", []string{"local_address", "local_port", "remote_address", "remote_port", "proto", "local_mark"}, nil).String(),
47 prometheus.NewDesc("node_ipvs_backend_connections_inactive", "The current inactive connections by local and remote address.", []string{"local_address", "local_port", "remote_address", "remote_port", "proto", "local_mark"}, nil).String(),
48 prometheus.NewDesc("node_ipvs_backend_weight", "The current backend weight by local and remote address.", []string{"local_address", "local_port", "remote_address", "remote_port", "proto", "local_mark"}, nil).String(),
49 },
50 nil,
51 },
52 {
53 "",
54 []string{
55 prometheus.NewDesc("node_ipvs_connections_total", "The total number of connections made.", nil, nil).String(),
56 prometheus.NewDesc("node_ipvs_incoming_packets_total", "The total number of incoming packets.", nil, nil).String(),
57 prometheus.NewDesc("node_ipvs_outgoing_packets_total", "The total number of outgoing packets.", nil, nil).String(),
58 prometheus.NewDesc("node_ipvs_incoming_bytes_total", "The total amount of incoming data.", nil, nil).String(),
59 prometheus.NewDesc("node_ipvs_outgoing_bytes_total", "The total amount of outgoing data.", nil, nil).String(),
60 prometheus.NewDesc("node_ipvs_backend_connections_active", "The current active connections by local and remote address.", nil, nil).String(),
61 prometheus.NewDesc("node_ipvs_backend_connections_inactive", "The current inactive connections by local and remote address.", nil, nil).String(),
62 prometheus.NewDesc("node_ipvs_backend_weight", "The current backend weight by local and remote address.", nil, nil).String(),
63 },
64 nil,
65 },
66 {
67 "local_port",
68 []string{
69 prometheus.NewDesc("node_ipvs_connections_total", "The total number of connections made.", nil, nil).String(),
70 prometheus.NewDesc("node_ipvs_incoming_packets_total", "The total number of incoming packets.", nil, nil).String(),
71 prometheus.NewDesc("node_ipvs_outgoing_packets_total", "The total number of outgoing packets.", nil, nil).String(),
72 prometheus.NewDesc("node_ipvs_incoming_bytes_total", "The total amount of incoming data.", nil, nil).String(),
73 prometheus.NewDesc("node_ipvs_outgoing_bytes_total", "The total amount of outgoing data.", nil, nil).String(),
74 prometheus.NewDesc("node_ipvs_backend_connections_active", "The current active connections by local and remote address.", []string{"local_port"}, nil).String(),
75 prometheus.NewDesc("node_ipvs_backend_connections_inactive", "The current inactive connections by local and remote address.", []string{"local_port"}, nil).String(),
76 prometheus.NewDesc("node_ipvs_backend_weight", "The current backend weight by local and remote address.", []string{"local_port"}, nil).String(),
77 },
78 nil,
79 },
80 {
81 "local_address,local_port",
82 []string{
83 prometheus.NewDesc("node_ipvs_connections_total", "The total number of connections made.", nil, nil).String(),
84 prometheus.NewDesc("node_ipvs_incoming_packets_total", "The total number of incoming packets.", nil, nil).String(),
85 prometheus.NewDesc("node_ipvs_outgoing_packets_total", "The total number of outgoing packets.", nil, nil).String(),
86 prometheus.NewDesc("node_ipvs_incoming_bytes_total", "The total amount of incoming data.", nil, nil).String(),
87 prometheus.NewDesc("node_ipvs_outgoing_bytes_total", "The total amount of outgoing data.", nil, nil).String(),
88 prometheus.NewDesc("node_ipvs_backend_connections_active", "The current active connections by local and remote address.", []string{"local_address", "local_port"}, nil).String(),
89 prometheus.NewDesc("node_ipvs_backend_connections_inactive", "The current inactive connections by local and remote address.", []string{"local_address", "local_port"}, nil).String(),
90 prometheus.NewDesc("node_ipvs_backend_weight", "The current backend weight by local and remote address.", []string{"local_address", "local_port"}, nil).String(),
91 },
92 nil,
93 },
94 {
95 "invalid_label",
96 nil,
97 errors.New(`unknown IPVS backend labels: "invalid_label"`),
98 },
99 {
100 "invalid_label,bad_label",
101 nil,
102 errors.New(`unknown IPVS backend labels: "bad_label, invalid_label"`),
103 },
37 } 104 }
38 sink := make(chan prometheus.Metric) 105 for _, test := range testcases {
39 go func() { 106 t.Run(test.labels, func(t *testing.T) {
40 err = collector.Update(sink) 107 args := []string{"--path.procfs", "fixtures/proc"}
41 if err != nil { 108 if test.labels != "<none>" {
42 panic(fmt.Sprintf("failed to update collector: %v", err)) 109 args = append(args, "--collector.ipvs.backend-labels="+test.labels)
43 } 110 }
44 }() 111 if _, err := kingpin.CommandLine.Parse(args); err != nil {
45 for expected, got := range map[string]string{ 112 t.Fatal(err)
46 prometheus.NewDesc("node_ipvs_connections_total", "The total number of connections made.", nil, nil).String(): (<-sink).Desc().String(), 113 }
47 prometheus.NewDesc("node_ipvs_incoming_packets_total", "The total number of incoming packets.", nil, nil).String(): (<-sink).Desc().String(), 114 collector, err := newIPVSCollector(log.NewNopLogger())
48 prometheus.NewDesc("node_ipvs_outgoing_packets_total", "The total number of outgoing packets.", nil, nil).String(): (<-sink).Desc().String(), 115 if err != nil {
49 prometheus.NewDesc("node_ipvs_incoming_bytes_total", "The total amount of incoming data.", nil, nil).String(): (<-sink).Desc().String(), 116 if test.err == nil {
50 prometheus.NewDesc("node_ipvs_outgoing_bytes_total", "The total amount of outgoing data.", nil, nil).String(): (<-sink).Desc().String(), 117 t.Fatal(err)
51 prometheus.NewDesc("node_ipvs_backend_connections_active", "The current active connections by local and remote address.", []string{"local_address", "local_port", "remote_address", "remote_port", "proto", "local_mark"}, nil).String(): (<-sink).Desc().String(), 118 }
52 prometheus.NewDesc("node_ipvs_backend_connections_inactive", "The current inactive connections by local and remote address.", []string{"local_address", "local_port", "remote_address", "remote_port", "proto", "local_mark"}, nil).String(): (<-sink).Desc().String(), 119 if !strings.Contains(err.Error(), test.err.Error()) {
53 prometheus.NewDesc("node_ipvs_backend_weight", "The current backend weight by local and remote address.", []string{"local_address", "local_port", "remote_address", "remote_port", "proto", "local_mark"}, nil).String(): (<-sink).Desc().String(), 120 t.Fatalf("expect error: %v contains %v", err, test.err)
54 } { 121 }
55 if expected != got { 122 return
56 t.Fatalf("Expected '%s' but got '%s'", expected, got) 123 }
57 } 124 if test.err != nil {
125 t.Fatalf("expect error: %v but got no error", test.err)
126 }
127
128 sink := make(chan prometheus.Metric)
129 go func() {
130 err = collector.Update(sink)
131 if err != nil {
132 panic(fmt.Sprintf("failed to update collector: %v", err))
133 }
134 }()
135 for _, expected := range test.expects {
136 got := (<-sink).Desc().String()
137 if expected != got {
138 t.Fatalf("Expected '%s' but got '%s'", expected, got)
139 }
140 }
141 })
58 } 142 }
59} 143}
60 144
@@ -77,44 +161,61 @@ func (c miniCollector) Describe(ch chan<- *prometheus.Desc) {
77} 161}
78 162
79func TestIPVSCollectorResponse(t *testing.T) { 163func TestIPVSCollectorResponse(t *testing.T) {
80 if _, err := kingpin.CommandLine.Parse([]string{"--path.procfs", "fixtures/proc"}); err != nil { 164 testcases := []struct {
81 t.Fatal(err) 165 labels string
166 metricsFile string
167 }{
168 {"<none>", "fixtures/ip_vs_result.txt"},
169 {"", "fixtures/ip_vs_result_lbs_none.txt"},
170 {"local_port", "fixtures/ip_vs_result_lbs_local_port.txt"},
171 {"local_address,local_port", "fixtures/ip_vs_result_lbs_local_address_local_port.txt"},
82 } 172 }
83 collector, err := NewIPVSCollector(log.NewNopLogger()) 173 for _, test := range testcases {
84 if err != nil { 174 t.Run(test.labels, func(t *testing.T) {
85 t.Fatal(err) 175 args := []string{"--path.procfs", "fixtures/proc"}
86 } 176 if test.labels != "<none>" {
87 prometheus.MustRegister(miniCollector{c: collector}) 177 args = append(args, "--collector.ipvs.backend-labels="+test.labels)
178 }
179 if _, err := kingpin.CommandLine.Parse(args); err != nil {
180 t.Fatal(err)
181 }
182 collector, err := NewIPVSCollector(log.NewNopLogger())
183 if err != nil {
184 t.Fatal(err)
185 }
186 registry := prometheus.NewRegistry()
187 registry.MustRegister(miniCollector{c: collector})
88 188
89 rw := httptest.NewRecorder() 189 rw := httptest.NewRecorder()
90 promhttp.Handler().ServeHTTP(rw, &http.Request{}) 190 promhttp.InstrumentMetricHandler(registry, promhttp.HandlerFor(registry, promhttp.HandlerOpts{})).ServeHTTP(rw, &http.Request{})
91 191
92 metricsFile := "fixtures/ip_vs_result.txt" 192 wantMetrics, err := ioutil.ReadFile(test.metricsFile)
93 wantMetrics, err := ioutil.ReadFile(metricsFile) 193 if err != nil {
94 if err != nil { 194 t.Fatalf("unable to read input test file %s: %s", test.metricsFile, err)
95 t.Fatalf("unable to read input test file %s: %s", metricsFile, err) 195 }
96 }
97 196
98 wantLines := strings.Split(string(wantMetrics), "\n") 197 wantLines := strings.Split(string(wantMetrics), "\n")
99 gotLines := strings.Split(string(rw.Body.String()), "\n") 198 gotLines := strings.Split(string(rw.Body.String()), "\n")
100 gotLinesIdx := 0 199 gotLinesIdx := 0
101 200
102 // Until the Prometheus Go client library offers better testability 201 // Until the Prometheus Go client library offers better testability
103 // (https://github.com/prometheus/client_golang/issues/58), we simply compare 202 // (https://github.com/prometheus/client_golang/issues/58), we simply compare
104 // verbatim text-format metrics outputs, but ignore any lines we don't have 203 // verbatim text-format metrics outputs, but ignore any lines we don't have
105 // in the fixture. Put differently, we are only testing that each line from 204 // in the fixture. Put differently, we are only testing that each line from
106 // the fixture is present, in the order given. 205 // the fixture is present, in the order given.
107wantLoop: 206 wantLoop:
108 for _, want := range wantLines { 207 for _, want := range wantLines {
109 for _, got := range gotLines[gotLinesIdx:] { 208 for _, got := range gotLines[gotLinesIdx:] {
110 if want == got { 209 if want == got {
111 // this is a line we are interested in, and it is correct 210 // this is a line we are interested in, and it is correct
112 continue wantLoop 211 continue wantLoop
113 } else { 212 } else {
114 gotLinesIdx++ 213 gotLinesIdx++
214 }
215 }
216 // if this point is reached, the line we want was missing
217 t.Fatalf("Missing expected output line(s), first missing line is %s", want)
115 } 218 }
116 } 219 })
117 // if this point is reached, the line we want was missing
118 t.Fatalf("Missing expected output line(s), first missing line is %s", want)
119 } 220 }
120} 221}