|
15 | 15 | package e2e
|
16 | 16 |
|
17 | 17 | import (
|
| 18 | + "bytes" |
18 | 19 | "context"
|
19 | 20 | "fmt"
|
| 21 | + "io" |
| 22 | + "net/http" |
| 23 | + "net/url" |
20 | 24 | "testing"
|
21 | 25 | "time"
|
22 | 26 |
|
| 27 | + dto "github.com/prometheus/client_model/go" |
| 28 | + "github.com/prometheus/common/expfmt" |
23 | 29 | "github.com/stretchr/testify/require"
|
24 | 30 |
|
25 | 31 | "go.etcd.io/etcd/api/v3/version"
|
@@ -126,3 +132,238 @@ func expectLearnerMetric(cx ctlCtx, procIdx int, expectMetric string) {
|
126 | 132 | args := e2e.CURLPrefixArgsCluster(cx.epc.Cfg, cx.epc.Procs[procIdx], "GET", e2e.CURLReq{Endpoint: "/metrics"})
|
127 | 133 | require.NoError(cx.t, e2e.SpawnWithExpectsContext(ctx, args, nil, expect.ExpectedResponse{Value: expectMetric}))
|
128 | 134 | }
|
| 135 | + |
| 136 | +func TestNoMetricsMissing(t *testing.T) { |
| 137 | + var ( |
| 138 | + // Note the list doesn't contain all the metrics, because the |
| 139 | + // labelled metrics won't be exposed by prometheus by default. |
| 140 | + // They are only exposed when at least one value with labels |
| 141 | + // is set. |
| 142 | + basicMetrics = []string{ |
| 143 | + "etcd_cluster_version", |
| 144 | + "etcd_debugging_auth_revision", |
| 145 | + "etcd_debugging_disk_backend_commit_rebalance_duration_seconds", |
| 146 | + "etcd_debugging_disk_backend_commit_spill_duration_seconds", |
| 147 | + "etcd_debugging_disk_backend_commit_write_duration_seconds", |
| 148 | + "etcd_debugging_lease_granted_total", |
| 149 | + "etcd_debugging_lease_renewed_total", |
| 150 | + "etcd_debugging_lease_revoked_total", |
| 151 | + "etcd_debugging_lease_ttl_total", |
| 152 | + "etcd_debugging_mvcc_compact_revision", |
| 153 | + "etcd_debugging_mvcc_current_revision", |
| 154 | + "etcd_debugging_mvcc_db_compaction_keys_total", |
| 155 | + "etcd_debugging_mvcc_db_compaction_last", |
| 156 | + "etcd_debugging_mvcc_db_compaction_pause_duration_milliseconds", |
| 157 | + "etcd_debugging_mvcc_db_compaction_total_duration_milliseconds", |
| 158 | + "etcd_debugging_mvcc_events_total", |
| 159 | + "etcd_debugging_mvcc_index_compaction_pause_duration_milliseconds", |
| 160 | + "etcd_debugging_mvcc_keys_total", |
| 161 | + "etcd_debugging_mvcc_pending_events_total", |
| 162 | + "etcd_debugging_mvcc_slow_watcher_total", |
| 163 | + "etcd_debugging_mvcc_total_put_size_in_bytes", |
| 164 | + "etcd_debugging_mvcc_watch_stream_total", |
| 165 | + "etcd_debugging_mvcc_watcher_total", |
| 166 | + "etcd_debugging_server_lease_expired_total", |
| 167 | + "etcd_debugging_snap_save_marshalling_duration_seconds", |
| 168 | + "etcd_debugging_snap_save_total_duration_seconds", |
| 169 | + "etcd_debugging_store_expires_total", |
| 170 | + "etcd_debugging_store_reads_total", |
| 171 | + "etcd_debugging_store_watch_requests_total", |
| 172 | + "etcd_debugging_store_watchers", |
| 173 | + "etcd_debugging_store_writes_total", |
| 174 | + "etcd_disk_backend_commit_duration_seconds", |
| 175 | + "etcd_disk_backend_defrag_duration_seconds", |
| 176 | + "etcd_disk_backend_snapshot_duration_seconds", |
| 177 | + "etcd_disk_defrag_inflight", |
| 178 | + "etcd_disk_wal_fsync_duration_seconds", |
| 179 | + "etcd_disk_wal_write_bytes_total", |
| 180 | + "etcd_disk_wal_write_duration_seconds", |
| 181 | + "etcd_grpc_proxy_cache_hits_total", |
| 182 | + "etcd_grpc_proxy_cache_keys_total", |
| 183 | + "etcd_grpc_proxy_cache_misses_total", |
| 184 | + "etcd_grpc_proxy_events_coalescing_total", |
| 185 | + "etcd_grpc_proxy_watchers_coalescing_total", |
| 186 | + "etcd_mvcc_db_open_read_transactions", |
| 187 | + "etcd_mvcc_db_total_size_in_bytes", |
| 188 | + "etcd_mvcc_db_total_size_in_use_in_bytes", |
| 189 | + "etcd_mvcc_delete_total", |
| 190 | + "etcd_mvcc_hash_duration_seconds", |
| 191 | + "etcd_mvcc_hash_rev_duration_seconds", |
| 192 | + "etcd_mvcc_put_total", |
| 193 | + "etcd_mvcc_range_total", |
| 194 | + "etcd_mvcc_txn_total", |
| 195 | + "etcd_network_client_grpc_received_bytes_total", |
| 196 | + "etcd_network_client_grpc_sent_bytes_total", |
| 197 | + "etcd_network_known_peers", |
| 198 | + "etcd_server_apply_duration_seconds", |
| 199 | + "etcd_server_client_requests_total", |
| 200 | + "etcd_server_go_version", |
| 201 | + "etcd_server_has_leader", |
| 202 | + "etcd_server_health_failures", |
| 203 | + "etcd_server_health_success", |
| 204 | + "etcd_server_heartbeat_send_failures_total", |
| 205 | + "etcd_server_id", |
| 206 | + "etcd_server_is_leader", |
| 207 | + "etcd_server_is_learner", |
| 208 | + "etcd_server_leader_changes_seen_total", |
| 209 | + "etcd_server_learner_promote_successes", |
| 210 | + "etcd_server_proposals_applied_total", |
| 211 | + "etcd_server_proposals_committed_total", |
| 212 | + "etcd_server_proposals_failed_total", |
| 213 | + "etcd_server_proposals_pending", |
| 214 | + "etcd_server_quota_backend_bytes", |
| 215 | + "etcd_server_range_duration_seconds", |
| 216 | + "etcd_server_read_indexes_failed_total", |
| 217 | + "etcd_server_slow_apply_total", |
| 218 | + "etcd_server_slow_read_indexes_total", |
| 219 | + "etcd_server_snapshot_apply_in_progress_total", |
| 220 | + "etcd_server_version", |
| 221 | + "etcd_snap_db_fsync_duration_seconds", |
| 222 | + "etcd_snap_db_save_total_duration_seconds", |
| 223 | + "etcd_snap_fsync_duration_seconds", |
| 224 | + "go_gc_duration_seconds", |
| 225 | + "go_gc_gogc_percent", |
| 226 | + "go_gc_gomemlimit_bytes", |
| 227 | + "go_goroutines", |
| 228 | + "go_info", |
| 229 | + "go_memstats_alloc_bytes", |
| 230 | + "go_memstats_alloc_bytes_total", |
| 231 | + "go_memstats_buck_hash_sys_bytes", |
| 232 | + "go_memstats_frees_total", |
| 233 | + "go_memstats_gc_sys_bytes", |
| 234 | + "go_memstats_heap_alloc_bytes", |
| 235 | + "go_memstats_heap_idle_bytes", |
| 236 | + "go_memstats_heap_inuse_bytes", |
| 237 | + "go_memstats_heap_objects", |
| 238 | + "go_memstats_heap_released_bytes", |
| 239 | + "go_memstats_heap_sys_bytes", |
| 240 | + "go_memstats_last_gc_time_seconds", |
| 241 | + "go_memstats_mallocs_total", |
| 242 | + "go_memstats_mcache_inuse_bytes", |
| 243 | + "go_memstats_mcache_sys_bytes", |
| 244 | + "go_memstats_mspan_inuse_bytes", |
| 245 | + "go_memstats_mspan_sys_bytes", |
| 246 | + "go_memstats_next_gc_bytes", |
| 247 | + "go_memstats_other_sys_bytes", |
| 248 | + "go_memstats_stack_inuse_bytes", |
| 249 | + "go_memstats_stack_sys_bytes", |
| 250 | + "go_memstats_sys_bytes", |
| 251 | + "go_sched_gomaxprocs_threads", |
| 252 | + "go_threads", |
| 253 | + "grpc_server_handled_total", |
| 254 | + "grpc_server_msg_received_total", |
| 255 | + "grpc_server_msg_sent_total", |
| 256 | + "grpc_server_started_total", |
| 257 | + "os_fd_limit", |
| 258 | + "os_fd_used", |
| 259 | + "promhttp_metric_handler_requests_in_flight", |
| 260 | + "promhttp_metric_handler_requests_total", |
| 261 | + } |
| 262 | + extraMultipleMemberClusterMetrics = []string{ |
| 263 | + "etcd_network_active_peers", |
| 264 | + "etcd_network_peer_received_bytes_total", |
| 265 | + "etcd_network_peer_sent_bytes_total", |
| 266 | + } |
| 267 | + extraExtensiveMetrics = []string{"grpc_server_handling_seconds"} |
| 268 | + ) |
| 269 | + |
| 270 | + testCases := []struct { |
| 271 | + name string |
| 272 | + options []e2e.EPClusterOption |
| 273 | + expectedMetrics []string |
| 274 | + }{ |
| 275 | + { |
| 276 | + name: "basic metrics of 1 member cluster", |
| 277 | + options: []e2e.EPClusterOption{ |
| 278 | + e2e.WithClusterSize(1), |
| 279 | + }, |
| 280 | + expectedMetrics: basicMetrics, |
| 281 | + }, |
| 282 | + { |
| 283 | + name: "basic metrics of 3 member cluster", |
| 284 | + options: []e2e.EPClusterOption{ |
| 285 | + e2e.WithClusterSize(3), |
| 286 | + }, |
| 287 | + expectedMetrics: append(basicMetrics, extraMultipleMemberClusterMetrics...), |
| 288 | + }, |
| 289 | + { |
| 290 | + name: "extensive metrics of 1 member cluster", |
| 291 | + options: []e2e.EPClusterOption{ |
| 292 | + e2e.WithClusterSize(1), |
| 293 | + e2e.WithExtensiveMetrics(), |
| 294 | + }, |
| 295 | + expectedMetrics: append(basicMetrics, extraExtensiveMetrics...), |
| 296 | + }, |
| 297 | + { |
| 298 | + name: "extensive metrics of 3 member cluster", |
| 299 | + options: []e2e.EPClusterOption{ |
| 300 | + e2e.WithClusterSize(3), |
| 301 | + e2e.WithExtensiveMetrics(), |
| 302 | + }, |
| 303 | + expectedMetrics: append(append(basicMetrics, extraExtensiveMetrics...), extraMultipleMemberClusterMetrics...), |
| 304 | + }, |
| 305 | + } |
| 306 | + |
| 307 | + for _, tc := range testCases { |
| 308 | + t.Run(tc.name, func(t *testing.T) { |
| 309 | + e2e.BeforeTest(t) |
| 310 | + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) |
| 311 | + defer cancel() |
| 312 | + |
| 313 | + epc, err := e2e.NewEtcdProcessCluster(ctx, t, tc.options...) |
| 314 | + require.NoError(t, err) |
| 315 | + defer epc.Close() |
| 316 | + |
| 317 | + c := epc.Procs[0].Etcdctl() |
| 318 | + for i := 0; i < 3; i++ { |
| 319 | + err = c.Put(ctx, fmt.Sprintf("key_%d", i), fmt.Sprintf("value_%d", i), config.PutOptions{}) |
| 320 | + require.NoError(t, err) |
| 321 | + } |
| 322 | + _, err = c.Get(ctx, "k", config.GetOptions{}) |
| 323 | + require.NoError(t, err) |
| 324 | + |
| 325 | + metricsURL, err := url.JoinPath(epc.Procs[0].Config().ClientURL, "metrics") |
| 326 | + require.NoError(t, err) |
| 327 | + |
| 328 | + mfs, err := getMetrics(metricsURL) |
| 329 | + require.NoError(t, err) |
| 330 | + |
| 331 | + var missingMetrics []string |
| 332 | + for _, metrics := range tc.expectedMetrics { |
| 333 | + if _, ok := mfs[metrics]; !ok { |
| 334 | + missingMetrics = append(missingMetrics, metrics) |
| 335 | + } |
| 336 | + } |
| 337 | + require.Emptyf(t, missingMetrics, "Some metrics are missing: %v", missingMetrics) |
| 338 | + |
| 339 | + // Please keep the log below to generate the expected metrics. |
| 340 | + // t.Logf("All metrics: %v", formatMetrics(slices.Sorted(maps.Keys(mfs)))) |
| 341 | + }) |
| 342 | + } |
| 343 | +} |
| 344 | + |
| 345 | +func getMetrics(metricsURL string) (map[string]*dto.MetricFamily, error) { |
| 346 | + httpClient := http.Client{Transport: &http.Transport{}} |
| 347 | + resp, err := httpClient.Get(metricsURL) |
| 348 | + if err != nil { |
| 349 | + return nil, err |
| 350 | + } |
| 351 | + |
| 352 | + data, err := io.ReadAll(resp.Body) |
| 353 | + if err != nil { |
| 354 | + return nil, err |
| 355 | + } |
| 356 | + |
| 357 | + var parser expfmt.TextParser |
| 358 | + return parser.TextToMetricFamilies(bytes.NewReader(data)) |
| 359 | +} |
| 360 | + |
| 361 | +// formatMetrics is only for test purpose |
| 362 | +/*func formatMetrics(metrics []string) string { |
| 363 | + quoted := make([]string, len(metrics)) |
| 364 | + for i, s := range metrics { |
| 365 | + quoted[i] = fmt.Sprintf(`"%s",`, s) |
| 366 | + } |
| 367 | +
|
| 368 | + return fmt.Sprintf("[]string{\n%s\n}", strings.Join(quoted, "\n")) |
| 369 | +}*/ |
0 commit comments