Skip to content

Commit c8646cd

Browse files
authored
Merge pull request #19242 from dims/test-for-etcd-metrics
Test for etcd metrics
2 parents 2471e41 + 19d405c commit c8646cd

File tree

1 file changed

+176
-0
lines changed

1 file changed

+176
-0
lines changed

tests/integration/clientv3/metrics_test.go

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,14 @@ import (
2121
"io"
2222
"net"
2323
"net/http"
24+
"slices"
2425
"strconv"
2526
"strings"
2627
"testing"
2728
"time"
2829

2930
grpcprom "github.com/grpc-ecosystem/go-grpc-prometheus"
31+
"github.com/prometheus/client_golang/prometheus"
3032
"github.com/prometheus/client_golang/prometheus/promhttp"
3133
"google.golang.org/grpc"
3234

@@ -176,3 +178,177 @@ func getHTTPBodyAsLines(t *testing.T, url string) []string {
176178
resp.Body.Close()
177179
return lines
178180
}
181+
182+
func TestAllMetricsGenerated(t *testing.T) {
183+
integration2.BeforeTest(t)
184+
185+
var (
186+
addr = "localhost:27989"
187+
ln net.Listener
188+
)
189+
190+
srv := &http.Server{Handler: promhttp.Handler()}
191+
srv.SetKeepAlivesEnabled(false)
192+
193+
ln, err := transport.NewUnixListener(addr)
194+
if err != nil {
195+
t.Errorf("Error: %v occurred while listening on addr: %v", err, addr)
196+
}
197+
198+
donec := make(chan struct{})
199+
defer func() {
200+
ln.Close()
201+
<-donec
202+
}()
203+
204+
// listen for all Prometheus metrics
205+
go func() {
206+
defer close(donec)
207+
208+
serr := srv.Serve(ln)
209+
if serr != nil && !transport.IsClosedConnError(serr) {
210+
t.Errorf("Err serving http requests: %v", serr)
211+
}
212+
}()
213+
214+
url := "unix://" + addr + "/metrics"
215+
216+
clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
217+
defer clus.Terminate(t)
218+
219+
clientMetrics := grpcprom.NewClientMetrics()
220+
prometheus.Register(clientMetrics)
221+
222+
cfg := clientv3.Config{
223+
Endpoints: []string{clus.Members[0].GRPCURL},
224+
DialOptions: []grpc.DialOption{
225+
grpc.WithUnaryInterceptor(clientMetrics.UnaryClientInterceptor()),
226+
grpc.WithStreamInterceptor(clientMetrics.StreamClientInterceptor()),
227+
},
228+
}
229+
cli, cerr := integration2.NewClient(t, cfg)
230+
if cerr != nil {
231+
t.Fatal(cerr)
232+
}
233+
defer cli.Close()
234+
235+
// Perform some operations to generate metrics
236+
wc := cli.Watch(context.Background(), "foo")
237+
_, err = cli.Put(context.Background(), "foo", "bar")
238+
if err != nil {
239+
t.Errorf("Error putting value in key store")
240+
}
241+
242+
// consume watch response
243+
select {
244+
case <-wc:
245+
case <-time.After(10 * time.Second):
246+
t.Error("Timeout occurred for getting watch response")
247+
}
248+
249+
// Define the expected list of metrics
250+
expectedMetrics := []string{
251+
"etcd_cluster_version",
252+
"etcd_disk_backend_commit_duration_seconds_bucket",
253+
"etcd_disk_backend_commit_duration_seconds_count",
254+
"etcd_disk_backend_commit_duration_seconds_sum",
255+
"etcd_disk_backend_defrag_duration_seconds_bucket",
256+
"etcd_disk_backend_defrag_duration_seconds_count",
257+
"etcd_disk_backend_defrag_duration_seconds_sum",
258+
"etcd_disk_backend_snapshot_duration_seconds_bucket",
259+
"etcd_disk_backend_snapshot_duration_seconds_count",
260+
"etcd_disk_backend_snapshot_duration_seconds_sum",
261+
"etcd_disk_defrag_inflight",
262+
"etcd_disk_wal_fsync_duration_seconds_bucket",
263+
"etcd_disk_wal_fsync_duration_seconds_count",
264+
"etcd_disk_wal_fsync_duration_seconds_sum",
265+
"etcd_disk_wal_write_bytes_total",
266+
"etcd_disk_wal_write_duration_seconds_bucket",
267+
"etcd_disk_wal_write_duration_seconds_count",
268+
"etcd_disk_wal_write_duration_seconds_sum",
269+
"etcd_mvcc_db_open_read_transactions",
270+
"etcd_mvcc_db_total_size_in_bytes",
271+
"etcd_mvcc_db_total_size_in_use_in_bytes",
272+
"etcd_mvcc_delete_total",
273+
"etcd_mvcc_hash_duration_seconds_bucket",
274+
"etcd_mvcc_hash_duration_seconds_count",
275+
"etcd_mvcc_hash_duration_seconds_sum",
276+
"etcd_mvcc_hash_rev_duration_seconds_bucket",
277+
"etcd_mvcc_hash_rev_duration_seconds_count",
278+
"etcd_mvcc_hash_rev_duration_seconds_sum",
279+
"etcd_mvcc_put_total",
280+
"etcd_mvcc_range_total",
281+
"etcd_mvcc_txn_total",
282+
"etcd_network_client_grpc_received_bytes_total",
283+
"etcd_network_client_grpc_sent_bytes_total",
284+
"etcd_network_known_peers",
285+
"etcd_server_apply_duration_seconds_bucket",
286+
"etcd_server_apply_duration_seconds_count",
287+
"etcd_server_apply_duration_seconds_sum",
288+
"etcd_server_client_requests_total",
289+
"etcd_server_go_version",
290+
"etcd_server_has_leader",
291+
"etcd_server_health_failures",
292+
"etcd_server_health_success",
293+
"etcd_server_heartbeat_send_failures_total",
294+
"etcd_server_id",
295+
"etcd_server_is_leader",
296+
"etcd_server_is_learner",
297+
"etcd_server_leader_changes_seen_total",
298+
"etcd_server_learner_promote_successes",
299+
"etcd_server_proposals_applied_total",
300+
"etcd_server_proposals_committed_total",
301+
"etcd_server_proposals_failed_total",
302+
"etcd_server_proposals_pending",
303+
"etcd_server_quota_backend_bytes",
304+
"etcd_server_read_indexes_failed_total",
305+
"etcd_server_slow_apply_total",
306+
"etcd_server_slow_read_indexes_total",
307+
"etcd_server_snapshot_apply_in_progress_total",
308+
"etcd_server_version",
309+
"etcd_snap_db_fsync_duration_seconds_bucket",
310+
"etcd_snap_db_fsync_duration_seconds_count",
311+
"etcd_snap_db_fsync_duration_seconds_sum",
312+
"etcd_snap_db_save_total_duration_seconds_bucket",
313+
"etcd_snap_db_save_total_duration_seconds_count",
314+
"etcd_snap_db_save_total_duration_seconds_sum",
315+
"etcd_snap_fsync_duration_seconds_bucket",
316+
"etcd_snap_fsync_duration_seconds_count",
317+
"etcd_snap_fsync_duration_seconds_sum",
318+
"grpc_client_handled_total",
319+
"grpc_client_msg_received_total",
320+
"grpc_client_msg_sent_total",
321+
"grpc_client_started_total",
322+
"grpc_server_handled_total",
323+
"grpc_server_msg_received_total",
324+
"grpc_server_msg_sent_total",
325+
"grpc_server_started_total",
326+
}
327+
328+
// Get the list of generated metrics
329+
generatedMetrics := getMetricsList(t, url)
330+
for _, metric := range expectedMetrics {
331+
if !slices.Contains(generatedMetrics, metric) {
332+
t.Errorf("Expected metric %s not found in generated metrics", metric)
333+
}
334+
}
335+
}
336+
337+
func getMetricsList(t *testing.T, url string) []string {
338+
lines := getHTTPBodyAsLines(t, url)
339+
metrics := make(map[string]struct{})
340+
for _, line := range lines {
341+
if strings.Contains(line, "{") {
342+
metric := line[:strings.Index(line, "{")]
343+
metrics[metric] = struct{}{}
344+
} else {
345+
metric := line[:strings.Index(line, " ")]
346+
metrics[metric] = struct{}{}
347+
}
348+
}
349+
var metricList []string
350+
for metric := range metrics {
351+
metricList = append(metricList, metric)
352+
}
353+
return metricList
354+
}

0 commit comments

Comments
 (0)