diff options
author | Wei He <weihe924stephen@gmail.com> | 2020-06-02 17:52:00 +0900 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-06-02 10:52:00 +0200 |
commit | 02532771213fcb024608043d7b1e514c1a80c2da (patch) | |
tree | 36e8a19a2f311deafca09763ea90a1cd3a260678 | |
parent | b7cb72adeb6b223dda8c0eb4bd2a16421b202d8e (diff) | |
download | prometheus_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.txt | 33 | ||||
-rw-r--r-- | collector/fixtures/ip_vs_result_lbs_local_port.txt | 27 | ||||
-rw-r--r-- | collector/fixtures/ip_vs_result_lbs_none.txt | 24 | ||||
-rw-r--r-- | collector/ipvs_linux.go | 121 | ||||
-rw-r--r-- | collector/ipvs_linux_test.go | 221 |
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 | ||
3 | node_ipvs_backend_connections_active{local_address="",local_port="0"} 385 | ||
4 | node_ipvs_backend_connections_active{local_address="192.168.0.22",local_port="3306"} 744 | ||
5 | node_ipvs_backend_connections_active{local_address="192.168.0.55",local_port="3306"} 0 | ||
6 | node_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 | ||
9 | node_ipvs_backend_connections_inactive{local_address="",local_port="0"} 6 | ||
10 | node_ipvs_backend_connections_inactive{local_address="192.168.0.22",local_port="3306"} 5 | ||
11 | node_ipvs_backend_connections_inactive{local_address="192.168.0.55",local_port="3306"} 0 | ||
12 | node_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 | ||
15 | node_ipvs_backend_weight{local_address="",local_port="0"} 120 | ||
16 | node_ipvs_backend_weight{local_address="192.168.0.22",local_port="3306"} 300 | ||
17 | node_ipvs_backend_weight{local_address="192.168.0.55",local_port="3306"} 100 | ||
18 | node_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 | ||
21 | node_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 | ||
24 | node_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 | ||
27 | node_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 | ||
30 | node_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 | ||
33 | node_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 | ||
3 | node_ipvs_backend_connections_active{local_port="0"} 385 | ||
4 | node_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 | ||
7 | node_ipvs_backend_connections_inactive{local_port="0"} 6 | ||
8 | node_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 | ||
11 | node_ipvs_backend_weight{local_port="0"} 120 | ||
12 | node_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 | ||
15 | node_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 | ||
18 | node_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 | ||
21 | node_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 | ||
24 | node_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 | ||
27 | node_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 | ||
3 | node_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 | ||
6 | node_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 | ||
9 | node_ipvs_backend_weight 720 | ||
10 | # HELP node_ipvs_connections_total The total number of connections made. | ||
11 | # TYPE node_ipvs_connections_total counter | ||
12 | node_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 | ||
15 | node_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 | ||
18 | node_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 | ||
21 | node_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 | ||
24 | node_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 | |||
18 | import ( | 18 | import ( |
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 | ||
29 | type ipvsCollector struct { | 32 | type 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 | ||
41 | type ipvsBackendStatus struct { | ||
42 | ActiveConn uint64 | ||
43 | InactConn uint64 | ||
44 | Weight uint64 | ||
45 | } | ||
46 | |||
47 | const ( | ||
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 | |||
56 | var ( | ||
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 | |||
37 | func init() { | 68 | func 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 | ||
47 | func newIPVSCollector(logger log.Logger) (*ipvsCollector, error) { | 78 | func 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 | |||
203 | func (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 @@ | |||
14 | package collector | 14 | package collector |
15 | 15 | ||
16 | import ( | 16 | import ( |
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 | ||
30 | func TestIPVSCollector(t *testing.T) { | 32 | func 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 | ||
79 | func TestIPVSCollectorResponse(t *testing.T) { | 163 | func 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. |
107 | wantLoop: | 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 | } |