From 163c5b94caa6d2095c576742735aca794ebfe9bc Mon Sep 17 00:00:00 2001 From: tzvonimir Date: Sat, 7 Mar 2026 23:30:07 +0100 Subject: [PATCH 1/3] Update health events --- gen/api/v1/metrics_collector.pb.go | 144 ++++++----- internal/collector/interface.go | 12 + internal/collector/pod_collector.go | 374 +++++++++++++++++++++++++++- proto/dakr_proto_descriptor.bin | Bin 343737 -> 344058 bytes 4 files changed, 466 insertions(+), 64 deletions(-) diff --git a/gen/api/v1/metrics_collector.pb.go b/gen/api/v1/metrics_collector.pb.go index e163d025..01857140 100644 --- a/gen/api/v1/metrics_collector.pb.go +++ b/gen/api/v1/metrics_collector.pb.go @@ -190,6 +190,10 @@ const ( ResourceType_RESOURCE_TYPE_WORKLOAD_RULE_CPU_THROTTLE ResourceType = 58 // CloudNativePG Cluster (postgresql.cnpg.io/v1) ResourceType_RESOURCE_TYPE_CNPG_CLUSTER ResourceType = 59 + // Container health events (direct path, not CRD-sourced) + ResourceType_RESOURCE_TYPE_CONTAINER_OOM_EVENT ResourceType = 60 + ResourceType_RESOURCE_TYPE_CONTAINER_CRASHLOOP_EVENT ResourceType = 61 + ResourceType_RESOURCE_TYPE_CONTAINER_STARTUP_LIFECYCLE ResourceType = 62 // Cluster snapshot type ResourceType_RESOURCE_TYPE_CLUSTER_SNAPSHOT ResourceType = 77 ) @@ -257,6 +261,9 @@ var ( 57: "RESOURCE_TYPE_WORKLOAD_RULE_OOM", 58: "RESOURCE_TYPE_WORKLOAD_RULE_CPU_THROTTLE", 59: "RESOURCE_TYPE_CNPG_CLUSTER", + 60: "RESOURCE_TYPE_CONTAINER_OOM_EVENT", + 61: "RESOURCE_TYPE_CONTAINER_CRASHLOOP_EVENT", + 62: "RESOURCE_TYPE_CONTAINER_STARTUP_LIFECYCLE", 77: "RESOURCE_TYPE_CLUSTER_SNAPSHOT", } ResourceType_value = map[string]int32{ @@ -320,6 +327,9 @@ var ( "RESOURCE_TYPE_WORKLOAD_RULE_OOM": 57, "RESOURCE_TYPE_WORKLOAD_RULE_CPU_THROTTLE": 58, "RESOURCE_TYPE_CNPG_CLUSTER": 59, + "RESOURCE_TYPE_CONTAINER_OOM_EVENT": 60, + "RESOURCE_TYPE_CONTAINER_CRASHLOOP_EVENT": 61, + "RESOURCE_TYPE_CONTAINER_STARTUP_LIFECYCLE": 62, "RESOURCE_TYPE_CLUSTER_SNAPSHOT": 77, } ) @@ -3562,7 +3572,7 @@ var file_api_v1_metrics_collector_proto_rawDesc = []byte{ 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x41, 0x49, 0x4e, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x54, 0x41, 0x52, 0x54, 0x45, 0x44, 0x10, 0x08, 0x12, 0x1f, 0x0a, 0x1b, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x53, 0x4e, - 0x41, 0x50, 0x53, 0x48, 0x4f, 0x54, 0x10, 0x09, 0x2a, 0x84, 0x10, 0x0a, 0x0c, 0x52, 0x65, 0x73, + 0x41, 0x50, 0x53, 0x48, 0x4f, 0x54, 0x10, 0x09, 0x2a, 0x87, 0x11, 0x0a, 0x0c, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x19, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x52, 0x45, 0x53, 0x4f, @@ -3688,70 +3698,78 @@ var file_api_v1_metrics_collector_proto_rawDesc = []byte{ 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x52, 0x55, 0x4c, 0x45, 0x5f, 0x43, 0x50, 0x55, 0x5f, 0x54, 0x48, 0x52, 0x4f, 0x54, 0x54, 0x4c, 0x45, 0x10, 0x3a, 0x12, 0x1e, 0x0a, 0x1a, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4e, 0x50, - 0x47, 0x5f, 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, 0x10, 0x3b, 0x12, 0x22, 0x0a, 0x1e, 0x52, - 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4c, 0x55, - 0x53, 0x54, 0x45, 0x52, 0x5f, 0x53, 0x4e, 0x41, 0x50, 0x53, 0x48, 0x4f, 0x54, 0x10, 0x4d, 0x2a, - 0x8c, 0x01, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x19, 0x0a, 0x15, - 0x4c, 0x4f, 0x47, 0x5f, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, - 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x4c, 0x4f, 0x47, 0x5f, 0x4c, - 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, - 0x4c, 0x4f, 0x47, 0x5f, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x02, - 0x12, 0x12, 0x0a, 0x0e, 0x4c, 0x4f, 0x47, 0x5f, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x57, 0x41, - 0x52, 0x4e, 0x10, 0x03, 0x12, 0x13, 0x0a, 0x0f, 0x4c, 0x4f, 0x47, 0x5f, 0x4c, 0x45, 0x56, 0x45, - 0x4c, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x04, 0x12, 0x13, 0x0a, 0x0f, 0x4c, 0x4f, 0x47, - 0x5f, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x46, 0x41, 0x54, 0x41, 0x4c, 0x10, 0x05, 0x32, 0xa0, - 0x05, 0x0a, 0x17, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, - 0x74, 0x6f, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x49, 0x0a, 0x0c, 0x53, 0x65, - 0x6e, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1b, 0x2e, 0x61, 0x70, 0x69, + 0x47, 0x5f, 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, 0x10, 0x3b, 0x12, 0x25, 0x0a, 0x21, 0x52, + 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4f, 0x4e, + 0x54, 0x41, 0x49, 0x4e, 0x45, 0x52, 0x5f, 0x4f, 0x4f, 0x4d, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, + 0x10, 0x3c, 0x12, 0x2b, 0x0a, 0x27, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, + 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x41, 0x49, 0x4e, 0x45, 0x52, 0x5f, 0x43, 0x52, + 0x41, 0x53, 0x48, 0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x10, 0x3d, 0x12, + 0x2d, 0x0a, 0x29, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, + 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x41, 0x49, 0x4e, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x52, 0x54, + 0x55, 0x50, 0x5f, 0x4c, 0x49, 0x46, 0x45, 0x43, 0x59, 0x43, 0x4c, 0x45, 0x10, 0x3e, 0x12, 0x22, + 0x0a, 0x1e, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, + 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x53, 0x4e, 0x41, 0x50, 0x53, 0x48, 0x4f, 0x54, + 0x10, 0x4d, 0x2a, 0x8c, 0x01, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, + 0x19, 0x0a, 0x15, 0x4c, 0x4f, 0x47, 0x5f, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x55, 0x4e, 0x53, + 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x4c, 0x4f, + 0x47, 0x5f, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x01, 0x12, + 0x12, 0x0a, 0x0e, 0x4c, 0x4f, 0x47, 0x5f, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x49, 0x4e, 0x46, + 0x4f, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x4c, 0x4f, 0x47, 0x5f, 0x4c, 0x45, 0x56, 0x45, 0x4c, + 0x5f, 0x57, 0x41, 0x52, 0x4e, 0x10, 0x03, 0x12, 0x13, 0x0a, 0x0f, 0x4c, 0x4f, 0x47, 0x5f, 0x4c, + 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x04, 0x12, 0x13, 0x0a, 0x0f, + 0x4c, 0x4f, 0x47, 0x5f, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x46, 0x41, 0x54, 0x41, 0x4c, 0x10, + 0x05, 0x32, 0xa0, 0x05, 0x0a, 0x17, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x43, 0x6f, 0x6c, + 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x49, 0x0a, + 0x0c, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1b, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x11, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x20, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x61, 0x0a, 0x14, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, - 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x4d, 0x65, - 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, - 0x74, 0x72, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x66, 0x0a, 0x19, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, - 0x72, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, - 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x1a, 0x29, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x6c, 0x75, 0x73, 0x74, - 0x65, 0x72, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, 0x12, 0x58, 0x0a, 0x11, 0x53, 0x65, - 0x6e, 0x64, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x4c, 0x6f, 0x67, 0x73, 0x12, - 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x65, 0x6c, - 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, - 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x70, 0x0a, 0x19, 0x53, 0x65, 0x6e, 0x64, 0x4e, 0x65, 0x74, 0x77, - 0x6f, 0x72, 0x6b, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, - 0x73, 0x12, 0x28, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4e, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x11, 0x53, 0x65, 0x6e, 0x64, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x20, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x61, 0x0a, 0x14, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, + 0x74, 0x72, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x23, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, + 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x24, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x65, 0x6c, + 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x66, 0x0a, 0x19, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x53, 0x74, 0x72, 0x65, + 0x61, 0x6d, 0x12, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, + 0x1a, 0x29, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x53, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, 0x12, 0x58, 0x0a, + 0x11, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x4c, 0x6f, + 0x67, 0x73, 0x12, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, + 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, + 0x6e, 0x64, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x4c, 0x6f, 0x67, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x70, 0x0a, 0x19, 0x53, 0x65, 0x6e, 0x64, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x4d, 0x65, 0x74, - 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, - 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x0c, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, - 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x42, 0x8e, 0x01, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x42, 0x15, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, - 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x64, 0x65, 0x76, 0x7a, 0x65, 0x72, 0x6f, 0x2d, 0x69, 0x6e, - 0x63, 0x2f, 0x7a, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x72, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x61, - 0x70, 0x69, 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x70, 0x69, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x41, 0x58, - 0x58, 0xaa, 0x02, 0x06, 0x41, 0x70, 0x69, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x06, 0x41, 0x70, 0x69, - 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x12, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, - 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x07, 0x41, 0x70, 0x69, 0x3a, 0x3a, - 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x72, 0x69, 0x63, 0x73, 0x12, 0x28, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, + 0x6e, 0x64, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, + 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x0c, 0x4e, 0x6f, 0x64, + 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x8e, 0x01, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x42, 0x15, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x43, 0x6f, 0x6c, 0x6c, + 0x65, 0x63, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x64, 0x65, 0x76, 0x7a, 0x65, 0x72, 0x6f, + 0x2d, 0x69, 0x6e, 0x63, 0x2f, 0x7a, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x72, 0x2f, 0x67, 0x65, + 0x6e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x70, 0x69, 0x76, 0x31, 0xa2, 0x02, + 0x03, 0x41, 0x58, 0x58, 0xaa, 0x02, 0x06, 0x41, 0x70, 0x69, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x06, + 0x41, 0x70, 0x69, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x12, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x31, 0x5c, + 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x07, 0x41, 0x70, + 0x69, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/internal/collector/interface.go b/internal/collector/interface.go index 793c8028..088888eb 100644 --- a/internal/collector/interface.go +++ b/internal/collector/interface.go @@ -146,6 +146,9 @@ const ( WorkloadRecommendation WorkloadRule CNPGCluster + ContainerOOMEvent + ContainerCrashLoopEvent + ContainerStartupLifecycle ) // String returns the string representation of the ResourceType @@ -206,6 +209,9 @@ func (r ResourceType) String() string { WorkloadRecommendation: "workload_recommendation", WorkloadRule: "workload_rule", CNPGCluster: "cnpg_cluster", + ContainerOOMEvent: "container_oom_event", + ContainerCrashLoopEvent: "container_crashloop_event", + ContainerStartupLifecycle: "container_startup_lifecycle", } if name, ok := names[r]; ok { @@ -327,6 +333,12 @@ func (r ResourceType) ProtoType() gen.ResourceType { return gen.ResourceType_RESOURCE_TYPE_WORKLOAD_RULE case CNPGCluster: return gen.ResourceType_RESOURCE_TYPE_CNPG_CLUSTER + case ContainerOOMEvent: + return gen.ResourceType_RESOURCE_TYPE_CONTAINER_OOM_EVENT + case ContainerCrashLoopEvent: + return gen.ResourceType_RESOURCE_TYPE_CONTAINER_CRASHLOOP_EVENT + case ContainerStartupLifecycle: + return gen.ResourceType_RESOURCE_TYPE_CONTAINER_STARTUP_LIFECYCLE default: return gen.ResourceType_RESOURCE_TYPE_UNSPECIFIED } diff --git a/internal/collector/pod_collector.go b/internal/collector/pod_collector.go index 3429de58..67f4885c 100644 --- a/internal/collector/pod_collector.go +++ b/internal/collector/pod_collector.go @@ -19,6 +19,30 @@ import ( "k8s.io/client-go/tools/cache" ) +// startupLifecycleKey uniquely identifies a container startup attempt. +type startupLifecycleKey struct { + podUID types.UID + containerName string + restartCount int32 +} + +// startupLifecycleEntry tracks phase transition timestamps for a single container startup. +type startupLifecycleEntry struct { + namespace string + workloadName string + workloadKind string + podName string + containerName string + restartCount int32 + isRestart bool + pendingAt *time.Time + creatingAt *time.Time + runningAt *time.Time + lastSeen time.Time +} + +const startupTrackerTTL = 30 * time.Minute + // PodCollector watches for pod events and collects pod data type PodCollector struct { client kubernetes.Interface @@ -33,6 +57,10 @@ type PodCollector struct { logger logr.Logger telemetryLogger telemetry_logger.Logger mu sync.RWMutex + + // Startup lifecycle tracking + startupTracker map[startupLifecycleKey]*startupLifecycleEntry + startupTrackerMu sync.Mutex } // NewPodCollector creates a new collector for pod resources @@ -77,6 +105,7 @@ func NewPodCollector( excludedPods: excludedPodsMap, logger: logger.WithName("pod-collector"), telemetryLogger: telemetryLogger, + startupTracker: make(map[startupLifecycleKey]*startupLifecycleEntry), } } @@ -186,6 +215,12 @@ func (c *PodCollector) handlePodEvent(pod *corev1.Pod, eventType EventType) { EventType: eventType, Key: fmt.Sprintf("%s/%s", pod.Namespace, pod.Name), } + + // On ADD (initial sync), emit startup lifecycle snapshots for running containers. + // This ensures lifecycle data is captured even if zxporter restarts. + if eventType == EventTypeAdd { + c.snapshotStartupLifecycles(pod) + } } // handlePodUpdate processes pod update events with special handling for container status changes @@ -197,8 +232,11 @@ func (c *PodCollector) handlePodUpdate(oldPod, newPod *corev1.Pod) { // Send the basic pod update c.handlePodEvent(newPod, EventTypeUpdate) - // Check for container events like OOMKilled + // Check for container events like OOMKilled and CrashLoopBackOff c.checkForContainerEvents(oldPod, newPod) + + // Track startup lifecycle phase transitions + c.trackStartupLifecycle(oldPod, newPod) } // checkForContainerEvents checks for container-specific events like OOMKilled @@ -262,10 +300,21 @@ func (c *PodCollector) checkForContainerEvents(oldPod, newPod *corev1.Pod) { }, ) } + + // Emit structured OOM event for direct path + c.emitContainerOOMEvent(newPod, newStatus) } c.sendContainerEvent(newPod, newStatus.Name, EventTypeContainerRestarted, &newStatus) } + + // Check for CrashLoopBackOff + if newStatus.State.Waiting != nil && newStatus.State.Waiting.Reason == "CrashLoopBackOff" { + // Only emit if this is a new CrashLoop state (wasn't in CrashLoop before) + if !exists || oldStatus.State.Waiting == nil || oldStatus.State.Waiting.Reason != "CrashLoopBackOff" { + c.emitContainerCrashLoopEvent(newPod, newStatus) + } + } } } @@ -370,3 +419,326 @@ func (c *PodCollector) AddResource(resource interface{}) error { c.handlePodEvent(pod, EventTypeAdd) return nil } + +// getWorkloadInfo extracts the top-level workload name and kind from a pod's owner references. +// For pods owned by ReplicaSets (which are owned by Deployments), it returns the Deployment info. +func getWorkloadInfo(pod *corev1.Pod) (name, kind string) { + if len(pod.OwnerReferences) == 0 { + return pod.Name, "Pod" + } + + owner := pod.OwnerReferences[0] + switch owner.Kind { + case "ReplicaSet": + // ReplicaSets created by Deployments have names like "-" + // Strip the hash suffix to get the Deployment name + rsName := owner.Name + if idx := strings.LastIndex(rsName, "-"); idx > 0 { + return rsName[:idx], "Deployment" + } + return rsName, "ReplicaSet" + case "StatefulSet": + return owner.Name, "StatefulSet" + case "DaemonSet": + return owner.Name, "DaemonSet" + case "Job": + return owner.Name, "Job" + default: + return owner.Name, owner.Kind + } +} + +// getContainerResources returns the memory request, limit, and usage for a container. +func getContainerResources(pod *corev1.Pod, containerName string) (requestBytes, limitBytes int64) { + for _, c := range pod.Spec.Containers { + if c.Name == containerName { + if req, ok := c.Resources.Requests[corev1.ResourceMemory]; ok { + requestBytes = req.Value() + } + if lim, ok := c.Resources.Limits[corev1.ResourceMemory]; ok { + limitBytes = lim.Value() + } + return + } + } + return 0, 0 +} + +// emitContainerOOMEvent sends a structured OOM event through the batch channel. +func (c *PodCollector) emitContainerOOMEvent(pod *corev1.Pod, status corev1.ContainerStatus) { + workloadName, workloadKind := getWorkloadInfo(pod) + requestBytes, limitBytes := getContainerResources(pod, status.Name) + + var exitCode int32 + var usageBytes int64 + if status.LastTerminationState.Terminated != nil { + exitCode = status.LastTerminationState.Terminated.ExitCode + } + // Memory usage at OOM time is not directly available from status; + // use the limit as an approximation (OOM means usage >= limit) + usageBytes = limitBytes + + c.batchChan <- CollectedResource{ + ResourceType: ContainerOOMEvent, + Object: map[string]interface{}{ + "namespace": pod.Namespace, + "workload_name": workloadName, + "workload_kind": workloadKind, + "pod_name": pod.Name, + "container_name": status.Name, + "memory_usage_bytes": usageBytes, + "memory_request_bytes": requestBytes, + "memory_limit_bytes": limitBytes, + "restart_count": status.RestartCount, + "exit_code": exitCode, + "timestamp": time.Now().Format(time.RFC3339Nano), + }, + Timestamp: time.Now(), + EventType: EventTypeAdd, + Key: fmt.Sprintf("oom/%s/%s/%s", pod.Namespace, pod.Name, status.Name), + } +} + +// emitContainerCrashLoopEvent sends a structured CrashLoopBackOff event through the batch channel. +func (c *PodCollector) emitContainerCrashLoopEvent(pod *corev1.Pod, status corev1.ContainerStatus) { + workloadName, workloadKind := getWorkloadInfo(pod) + + var lastTerminationReason string + var exitCode int32 + var isOOMRelated bool + if status.LastTerminationState.Terminated != nil { + lastTerminationReason = status.LastTerminationState.Terminated.Reason + exitCode = status.LastTerminationState.Terminated.ExitCode + isOOMRelated = lastTerminationReason == "OOMKilled" + } + + c.logger.Info("Container CrashLoopBackOff detected", + "namespace", pod.Namespace, + "pod", pod.Name, + "container", status.Name, + "restartCount", status.RestartCount, + "isOOMRelated", isOOMRelated) + + c.batchChan <- CollectedResource{ + ResourceType: ContainerCrashLoopEvent, + Object: map[string]interface{}{ + "namespace": pod.Namespace, + "workload_name": workloadName, + "workload_kind": workloadKind, + "pod_name": pod.Name, + "container_name": status.Name, + "restart_count": status.RestartCount, + "last_termination_reason": lastTerminationReason, + "exit_code": exitCode, + "is_oom_related": isOOMRelated, + "timestamp": time.Now().Format(time.RFC3339Nano), + }, + Timestamp: time.Now(), + EventType: EventTypeAdd, + Key: fmt.Sprintf("crashloop/%s/%s/%s", pod.Namespace, pod.Name, status.Name), + } +} + +// trackStartupLifecycle tracks container startup phase transitions and emits lifecycle events. +func (c *PodCollector) trackStartupLifecycle(oldPod, newPod *corev1.Pod) { + now := time.Now() + + // Periodically clean up stale entries + c.cleanupStartupTracker() + + for _, newStatus := range newPod.Status.ContainerStatuses { + key := startupLifecycleKey{ + podUID: newPod.UID, + containerName: newStatus.Name, + restartCount: newStatus.RestartCount, + } + + c.startupTrackerMu.Lock() + entry, exists := c.startupTracker[key] + + if !exists { + // New lifecycle entry — pod is in Pending or later phase + workloadName, workloadKind := getWorkloadInfo(newPod) + entry = &startupLifecycleEntry{ + namespace: newPod.Namespace, + workloadName: workloadName, + workloadKind: workloadKind, + podName: newPod.Name, + containerName: newStatus.Name, + restartCount: newStatus.RestartCount, + isRestart: newStatus.RestartCount > 0, + lastSeen: now, + } + + // Record pending time from pod condition + for _, cond := range newPod.Status.Conditions { + if cond.Type == corev1.PodScheduled && cond.Status == corev1.ConditionTrue { + t := cond.LastTransitionTime.Time + entry.pendingAt = &t + break + } + } + + c.startupTracker[key] = entry + } + + entry.lastSeen = now + + // Track ContainerCreating phase + if newStatus.State.Waiting != nil && newStatus.State.Waiting.Reason == "ContainerCreating" && entry.creatingAt == nil { + entry.creatingAt = &now + } + + // Track Running phase + if newStatus.State.Running != nil && entry.runningAt == nil { + t := newStatus.State.Running.StartedAt.Time + if t.IsZero() { + t = now + } + entry.runningAt = &t + } + + // Track Ready phase — emit the lifecycle event + if newStatus.Ready && entry.runningAt != nil { + // Calculate durations + var timeToRunningMs, timeToReadyMs *int64 + if entry.pendingAt != nil && entry.runningAt != nil { + ms := entry.runningAt.Sub(*entry.pendingAt).Milliseconds() + timeToRunningMs = &ms + } + if entry.pendingAt != nil { + ms := now.Sub(*entry.pendingAt).Milliseconds() + timeToReadyMs = &ms + } + + c.emitStartupLifecycleEvent(entry, &now, timeToRunningMs, timeToReadyMs) + + // Clean up the entry + delete(c.startupTracker, key) + c.startupTrackerMu.Unlock() + continue + } + + c.startupTrackerMu.Unlock() + } +} + +// emitStartupLifecycleEvent sends a completed startup lifecycle event through the batch channel. +func (c *PodCollector) emitStartupLifecycleEvent(entry *startupLifecycleEntry, readyAt *time.Time, timeToRunningMs, timeToReadyMs *int64) { + payload := map[string]interface{}{ + "namespace": entry.namespace, + "workload_name": entry.workloadName, + "workload_kind": entry.workloadKind, + "pod_name": entry.podName, + "container_name": entry.containerName, + "restart_count": entry.restartCount, + "is_restart": entry.isRestart, + "timestamp": time.Now().Format(time.RFC3339Nano), + } + + if entry.pendingAt != nil { + payload["pending_at"] = entry.pendingAt.Format(time.RFC3339Nano) + } + if entry.creatingAt != nil { + payload["container_creating_at"] = entry.creatingAt.Format(time.RFC3339Nano) + } + if entry.runningAt != nil { + payload["running_at"] = entry.runningAt.Format(time.RFC3339Nano) + } + if readyAt != nil { + payload["ready_at"] = readyAt.Format(time.RFC3339Nano) + } + if timeToRunningMs != nil { + payload["time_to_running_ms"] = *timeToRunningMs + } + if timeToReadyMs != nil { + payload["time_to_ready_ms"] = *timeToReadyMs + } + + c.batchChan <- CollectedResource{ + ResourceType: ContainerStartupLifecycle, + Object: payload, + Timestamp: time.Now(), + EventType: EventTypeAdd, + Key: fmt.Sprintf("startup/%s/%s/%s/%d", entry.namespace, entry.podName, entry.containerName, entry.restartCount), + } +} + +// cleanupStartupTracker removes stale entries that never reached Ready state. +func (c *PodCollector) cleanupStartupTracker() { + c.startupTrackerMu.Lock() + defer c.startupTrackerMu.Unlock() + + now := time.Now() + for key, entry := range c.startupTracker { + if now.Sub(entry.lastSeen) > startupTrackerTTL { + delete(c.startupTracker, key) + } + } +} + +// snapshotStartupLifecycles reconstructs and emits startup lifecycle events from a pod's +// current status. Called during initial cache sync (ADD events) so that lifecycle data +// is captured for containers that started before zxporter was running. The DB upsert +// (ON CONFLICT DO NOTHING) ensures no duplicates if the event-driven path already captured it. +func (c *PodCollector) snapshotStartupLifecycles(pod *corev1.Pod) { + // Only emit for pods that have started + if pod.Status.StartTime == nil { + return + } + + // Find the Ready condition for ready_at timestamp + var readyAt *time.Time + for _, cond := range pod.Status.Conditions { + if cond.Type == corev1.ContainersReady && cond.Status == corev1.ConditionTrue { + t := cond.LastTransitionTime.Time + if !t.IsZero() { + readyAt = &t + } + break + } + } + + workloadName, workloadKind := getWorkloadInfo(pod) + pendingAt := pod.Status.StartTime.Time + + for _, cs := range pod.Status.ContainerStatuses { + // Only snapshot containers that are running AND ready. + // Non-ready containers are left for the event-driven path to capture, + // avoiding partial records that would block the full record via DoNothing upsert. + if cs.State.Running == nil || !cs.Ready || readyAt == nil { + continue + } + + runningAt := cs.State.Running.StartedAt.Time + if runningAt.IsZero() { + continue + } + + entry := &startupLifecycleEntry{ + namespace: pod.Namespace, + workloadName: workloadName, + workloadKind: workloadKind, + podName: pod.Name, + containerName: cs.Name, + restartCount: cs.RestartCount, + isRestart: cs.RestartCount > 0, + pendingAt: &pendingAt, + runningAt: &runningAt, + } + + var timeToRunningMs, timeToReadyMs *int64 + + ms := runningAt.Sub(pendingAt).Milliseconds() + if ms > 0 { + timeToRunningMs = &ms + } + + ms = readyAt.Sub(pendingAt).Milliseconds() + if ms > 0 { + timeToReadyMs = &ms + } + + c.emitStartupLifecycleEvent(entry, readyAt, timeToRunningMs, timeToReadyMs) + } +} diff --git a/proto/dakr_proto_descriptor.bin b/proto/dakr_proto_descriptor.bin index 68f9cfd437712e9d7325820ef25e1bf1f8574bd3..693524b64bcc3f648f988d58e6f56d35f50edd48 100644 GIT binary patch delta 10675 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 From 1fd2e190a2797eb3ce4df9fc2bd52ff9df1e793a Mon Sep 17 00:00:00 2001 From: tzvonimir Date: Sun, 8 Mar 2026 00:24:14 +0100 Subject: [PATCH 2/3] Update collector --- gen/api/v1/metrics_collector.pb.go | 144 +++++++++--------- .../collector/container_resource_collector.go | 75 +++++++++ internal/collector/interface.go | 4 + proto/dakr_proto_descriptor.bin | Bin 344058 -> 344150 bytes 4 files changed, 154 insertions(+), 69 deletions(-) diff --git a/gen/api/v1/metrics_collector.pb.go b/gen/api/v1/metrics_collector.pb.go index 01857140..f3fa9cea 100644 --- a/gen/api/v1/metrics_collector.pb.go +++ b/gen/api/v1/metrics_collector.pb.go @@ -191,9 +191,10 @@ const ( // CloudNativePG Cluster (postgresql.cnpg.io/v1) ResourceType_RESOURCE_TYPE_CNPG_CLUSTER ResourceType = 59 // Container health events (direct path, not CRD-sourced) - ResourceType_RESOURCE_TYPE_CONTAINER_OOM_EVENT ResourceType = 60 - ResourceType_RESOURCE_TYPE_CONTAINER_CRASHLOOP_EVENT ResourceType = 61 - ResourceType_RESOURCE_TYPE_CONTAINER_STARTUP_LIFECYCLE ResourceType = 62 + ResourceType_RESOURCE_TYPE_CONTAINER_OOM_EVENT ResourceType = 60 + ResourceType_RESOURCE_TYPE_CONTAINER_CRASHLOOP_EVENT ResourceType = 61 + ResourceType_RESOURCE_TYPE_CONTAINER_STARTUP_LIFECYCLE ResourceType = 62 + ResourceType_RESOURCE_TYPE_CONTAINER_CPU_THROTTLE_EVENT ResourceType = 63 // Cluster snapshot type ResourceType_RESOURCE_TYPE_CLUSTER_SNAPSHOT ResourceType = 77 ) @@ -264,73 +265,75 @@ var ( 60: "RESOURCE_TYPE_CONTAINER_OOM_EVENT", 61: "RESOURCE_TYPE_CONTAINER_CRASHLOOP_EVENT", 62: "RESOURCE_TYPE_CONTAINER_STARTUP_LIFECYCLE", + 63: "RESOURCE_TYPE_CONTAINER_CPU_THROTTLE_EVENT", 77: "RESOURCE_TYPE_CLUSTER_SNAPSHOT", } ResourceType_value = map[string]int32{ - "RESOURCE_TYPE_UNSPECIFIED": 0, - "RESOURCE_TYPE_NODE": 1, - "RESOURCE_TYPE_POD": 2, - "RESOURCE_TYPE_NAMESPACE": 3, - "RESOURCE_TYPE_EVENT": 4, - "RESOURCE_TYPE_ENDPOINTS": 5, - "RESOURCE_TYPE_SERVICE_ACCOUNT": 6, - "RESOURCE_TYPE_LIMIT_RANGE": 7, - "RESOURCE_TYPE_RESOURCE_QUOTA": 8, - "RESOURCE_TYPE_DEPLOYMENT": 9, - "RESOURCE_TYPE_STATEFUL_SET": 10, - "RESOURCE_TYPE_DAEMON_SET": 11, - "RESOURCE_TYPE_REPLICA_SET": 12, - "RESOURCE_TYPE_REPLICATION_CONTROLLER": 13, - "RESOURCE_TYPE_JOB": 14, - "RESOURCE_TYPE_CRON_JOB": 15, - "RESOURCE_TYPE_PERSISTENT_VOLUME_CLAIM": 16, - "RESOURCE_TYPE_PERSISTENT_VOLUME": 17, - "RESOURCE_TYPE_STORAGE_CLASS": 18, - "RESOURCE_TYPE_SERVICE": 19, - "RESOURCE_TYPE_INGRESS": 20, - "RESOURCE_TYPE_INGRESS_CLASS": 21, - "RESOURCE_TYPE_NETWORK_POLICY": 22, - "RESOURCE_TYPE_ROLE": 23, - "RESOURCE_TYPE_ROLE_BINDING": 24, - "RESOURCE_TYPE_CLUSTER_ROLE": 25, - "RESOURCE_TYPE_CLUSTER_ROLE_BINDING": 26, - "RESOURCE_TYPE_HORIZONTAL_POD_AUTOSCALER": 27, - "RESOURCE_TYPE_VERTICAL_POD_AUTOSCALER": 28, - "RESOURCE_TYPE_POD_DISRUPTION_BUDGET": 29, - "RESOURCE_TYPE_POD_SECURITY_POLICY": 30, - "RESOURCE_TYPE_CUSTOM_RESOURCE_DEFINITION": 31, - "RESOURCE_TYPE_CUSTOM_RESOURCE": 32, - "RESOURCE_TYPE_CONFIG_MAP": 33, - "RESOURCE_TYPE_SECRET": 34, - "RESOURCE_TYPE_CONTAINER": 35, - "RESOURCE_TYPE_NODE_RESOURCE": 36, - "RESOURCE_TYPE_CONTAINER_RESOURCE": 37, - "RESOURCE_TYPE_CLUSTER": 38, - "RESOURCE_TYPE_CSI_NODE": 39, - "RESOURCE_TYPE_KARPENTER": 40, - "RESOURCE_TYPE_DATADOG": 41, - "RESOURCE_TYPE_ARGO_ROLLOUTS": 42, - "RESOURCE_TYPE_KEDA": 43, - "RESOURCE_TYPE_KEDA_SCALED_OBJECT": 44, - "RESOURCE_TYPE_KEDA_SCALED_JOB": 45, - "RESOURCE_TYPE_CSI_DRIVER": 46, - "RESOURCE_TYPE_CSI_STORAGE_CAPACITY": 47, - "RESOURCE_TYPE_VOLUME_ATTACHMENT": 48, - "RESOURCE_TYPE_KUBEFLOW_NOTEBOOK": 49, - "RESOURCE_TYPE_VOLCANO_JOB": 50, - "RESOURCE_TYPE_SPARK_APPLICATION": 51, - "RESOURCE_TYPE_SCHEDULED_SPARK_APPLICATION": 52, - "RESOURCE_TYPE_CRON_VOLCANO_JOB": 53, - "RESOURCE_TYPE_PVC_METRICS": 54, - "RESOURCE_TYPE_WORKLOAD_RECOMMENDATION": 55, - "RESOURCE_TYPE_WORKLOAD_RULE": 56, - "RESOURCE_TYPE_WORKLOAD_RULE_OOM": 57, - "RESOURCE_TYPE_WORKLOAD_RULE_CPU_THROTTLE": 58, - "RESOURCE_TYPE_CNPG_CLUSTER": 59, - "RESOURCE_TYPE_CONTAINER_OOM_EVENT": 60, - "RESOURCE_TYPE_CONTAINER_CRASHLOOP_EVENT": 61, - "RESOURCE_TYPE_CONTAINER_STARTUP_LIFECYCLE": 62, - "RESOURCE_TYPE_CLUSTER_SNAPSHOT": 77, + "RESOURCE_TYPE_UNSPECIFIED": 0, + "RESOURCE_TYPE_NODE": 1, + "RESOURCE_TYPE_POD": 2, + "RESOURCE_TYPE_NAMESPACE": 3, + "RESOURCE_TYPE_EVENT": 4, + "RESOURCE_TYPE_ENDPOINTS": 5, + "RESOURCE_TYPE_SERVICE_ACCOUNT": 6, + "RESOURCE_TYPE_LIMIT_RANGE": 7, + "RESOURCE_TYPE_RESOURCE_QUOTA": 8, + "RESOURCE_TYPE_DEPLOYMENT": 9, + "RESOURCE_TYPE_STATEFUL_SET": 10, + "RESOURCE_TYPE_DAEMON_SET": 11, + "RESOURCE_TYPE_REPLICA_SET": 12, + "RESOURCE_TYPE_REPLICATION_CONTROLLER": 13, + "RESOURCE_TYPE_JOB": 14, + "RESOURCE_TYPE_CRON_JOB": 15, + "RESOURCE_TYPE_PERSISTENT_VOLUME_CLAIM": 16, + "RESOURCE_TYPE_PERSISTENT_VOLUME": 17, + "RESOURCE_TYPE_STORAGE_CLASS": 18, + "RESOURCE_TYPE_SERVICE": 19, + "RESOURCE_TYPE_INGRESS": 20, + "RESOURCE_TYPE_INGRESS_CLASS": 21, + "RESOURCE_TYPE_NETWORK_POLICY": 22, + "RESOURCE_TYPE_ROLE": 23, + "RESOURCE_TYPE_ROLE_BINDING": 24, + "RESOURCE_TYPE_CLUSTER_ROLE": 25, + "RESOURCE_TYPE_CLUSTER_ROLE_BINDING": 26, + "RESOURCE_TYPE_HORIZONTAL_POD_AUTOSCALER": 27, + "RESOURCE_TYPE_VERTICAL_POD_AUTOSCALER": 28, + "RESOURCE_TYPE_POD_DISRUPTION_BUDGET": 29, + "RESOURCE_TYPE_POD_SECURITY_POLICY": 30, + "RESOURCE_TYPE_CUSTOM_RESOURCE_DEFINITION": 31, + "RESOURCE_TYPE_CUSTOM_RESOURCE": 32, + "RESOURCE_TYPE_CONFIG_MAP": 33, + "RESOURCE_TYPE_SECRET": 34, + "RESOURCE_TYPE_CONTAINER": 35, + "RESOURCE_TYPE_NODE_RESOURCE": 36, + "RESOURCE_TYPE_CONTAINER_RESOURCE": 37, + "RESOURCE_TYPE_CLUSTER": 38, + "RESOURCE_TYPE_CSI_NODE": 39, + "RESOURCE_TYPE_KARPENTER": 40, + "RESOURCE_TYPE_DATADOG": 41, + "RESOURCE_TYPE_ARGO_ROLLOUTS": 42, + "RESOURCE_TYPE_KEDA": 43, + "RESOURCE_TYPE_KEDA_SCALED_OBJECT": 44, + "RESOURCE_TYPE_KEDA_SCALED_JOB": 45, + "RESOURCE_TYPE_CSI_DRIVER": 46, + "RESOURCE_TYPE_CSI_STORAGE_CAPACITY": 47, + "RESOURCE_TYPE_VOLUME_ATTACHMENT": 48, + "RESOURCE_TYPE_KUBEFLOW_NOTEBOOK": 49, + "RESOURCE_TYPE_VOLCANO_JOB": 50, + "RESOURCE_TYPE_SPARK_APPLICATION": 51, + "RESOURCE_TYPE_SCHEDULED_SPARK_APPLICATION": 52, + "RESOURCE_TYPE_CRON_VOLCANO_JOB": 53, + "RESOURCE_TYPE_PVC_METRICS": 54, + "RESOURCE_TYPE_WORKLOAD_RECOMMENDATION": 55, + "RESOURCE_TYPE_WORKLOAD_RULE": 56, + "RESOURCE_TYPE_WORKLOAD_RULE_OOM": 57, + "RESOURCE_TYPE_WORKLOAD_RULE_CPU_THROTTLE": 58, + "RESOURCE_TYPE_CNPG_CLUSTER": 59, + "RESOURCE_TYPE_CONTAINER_OOM_EVENT": 60, + "RESOURCE_TYPE_CONTAINER_CRASHLOOP_EVENT": 61, + "RESOURCE_TYPE_CONTAINER_STARTUP_LIFECYCLE": 62, + "RESOURCE_TYPE_CONTAINER_CPU_THROTTLE_EVENT": 63, + "RESOURCE_TYPE_CLUSTER_SNAPSHOT": 77, } ) @@ -3572,7 +3575,7 @@ var file_api_v1_metrics_collector_proto_rawDesc = []byte{ 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x41, 0x49, 0x4e, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x54, 0x41, 0x52, 0x54, 0x45, 0x44, 0x10, 0x08, 0x12, 0x1f, 0x0a, 0x1b, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x53, 0x4e, - 0x41, 0x50, 0x53, 0x48, 0x4f, 0x54, 0x10, 0x09, 0x2a, 0x87, 0x11, 0x0a, 0x0c, 0x52, 0x65, 0x73, + 0x41, 0x50, 0x53, 0x48, 0x4f, 0x54, 0x10, 0x09, 0x2a, 0xb7, 0x11, 0x0a, 0x0c, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x19, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x52, 0x45, 0x53, 0x4f, @@ -3706,7 +3709,10 @@ var file_api_v1_metrics_collector_proto_rawDesc = []byte{ 0x41, 0x53, 0x48, 0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x10, 0x3d, 0x12, 0x2d, 0x0a, 0x29, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x41, 0x49, 0x4e, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x52, 0x54, - 0x55, 0x50, 0x5f, 0x4c, 0x49, 0x46, 0x45, 0x43, 0x59, 0x43, 0x4c, 0x45, 0x10, 0x3e, 0x12, 0x22, + 0x55, 0x50, 0x5f, 0x4c, 0x49, 0x46, 0x45, 0x43, 0x59, 0x43, 0x4c, 0x45, 0x10, 0x3e, 0x12, 0x2e, + 0x0a, 0x2a, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, + 0x43, 0x4f, 0x4e, 0x54, 0x41, 0x49, 0x4e, 0x45, 0x52, 0x5f, 0x43, 0x50, 0x55, 0x5f, 0x54, 0x48, + 0x52, 0x4f, 0x54, 0x54, 0x4c, 0x45, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x10, 0x3f, 0x12, 0x22, 0x0a, 0x1e, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x53, 0x4e, 0x41, 0x50, 0x53, 0x48, 0x4f, 0x54, 0x10, 0x4d, 0x2a, 0x8c, 0x01, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, diff --git a/internal/collector/container_resource_collector.go b/internal/collector/container_resource_collector.go index 64e2a046..d41c35df 100644 --- a/internal/collector/container_resource_collector.go +++ b/internal/collector/container_resource_collector.go @@ -55,6 +55,12 @@ type gpuQueryState struct { lastFailed bool } +// throttleTracker tracks last emission time for CPU throttle events per container to avoid duplicates. +type throttleTracker struct { + lastEmitted map[string]time.Time // key: "ns/pod/container" → last emit time + mu sync.Mutex +} + // ContainerResourceCollector collects container resource usage metrics type ContainerResourceCollector struct { k8sClient kubernetes.Interface @@ -76,6 +82,7 @@ type ContainerResourceCollector struct { mu sync.RWMutex gpuQueryErrorState map[string]*gpuQueryState // most of the case we are not deploying zxporter to GPU nodes which cause GPU query to fail infinitely, and we dont want to get that GPU query fails error every minute for every container gpuQueryMu sync.Mutex + throttle throttleTracker // Removed manual cache rsLister appslisters.ReplicaSetLister rsInformer cache.SharedIndexInformer @@ -145,6 +152,7 @@ func NewContainerResourceCollector( metrics: metrics, telemetryLogger: telemetryLogger, gpuQueryErrorState: make(map[string]*gpuQueryState), + throttle: throttleTracker{lastEmitted: make(map[string]time.Time)}, } } @@ -381,6 +389,11 @@ func (c *ContainerResourceCollector) collectAllContainerResources(ctx context.Co throttleFraction = 0 } + // Emit CPU throttle event if fraction exceeds threshold + if throttleFraction > 0.1 { + c.emitCPUThrottleEvent(pod, containerMetrics, throttleFraction) + } + // Fetch I/O metrics for this container if !c.config.DisableNetworkIOMetrics { ioMetrics, err = c.collectContainerIOMetrics(queryCtx, pod, containerMetrics.Name) @@ -776,6 +789,68 @@ func (c *ContainerResourceCollector) collectContainerCPUThrottleMetrics(ctx cont return fraction, nil } +// emitCPUThrottleEvent sends a CPU throttle event through the batch channel with 5-minute deduplication. +func (c *ContainerResourceCollector) emitCPUThrottleEvent(pod *corev1.Pod, containerMetrics metricsv1beta1.ContainerMetrics, throttleFraction float64) { + dedupKey := fmt.Sprintf("%s/%s/%s", pod.Namespace, pod.Name, containerMetrics.Name) + + c.throttle.mu.Lock() + if lastEmit, ok := c.throttle.lastEmitted[dedupKey]; ok && time.Since(lastEmit) < 5*time.Minute { + c.throttle.mu.Unlock() + return + } + c.throttle.lastEmitted[dedupKey] = time.Now() + c.throttle.mu.Unlock() + + // Resolve workload info + workloadKind, workloadName := c.resolveWorkload(pod) + if workloadKind == "" { + workloadKind = "Pod" + workloadName = pod.Name + } + + // Get CPU resources from container spec + var cpuUsageMillis, cpuRequestMillis, cpuLimitMillis int64 + cpuUsageMillis = containerMetrics.Usage.Cpu().MilliValue() + for i := range pod.Spec.Containers { + if pod.Spec.Containers[i].Name == containerMetrics.Name { + if pod.Spec.Containers[i].Resources.Requests != nil { + if cpu := pod.Spec.Containers[i].Resources.Requests.Cpu(); cpu != nil { + cpuRequestMillis = cpu.MilliValue() + } + } + if pod.Spec.Containers[i].Resources.Limits != nil { + if cpu := pod.Spec.Containers[i].Resources.Limits.Cpu(); cpu != nil { + cpuLimitMillis = cpu.MilliValue() + } + } + break + } + } + + // Round timestamp to nearest minute for DB dedup + now := time.Now() + roundedTS := now.Truncate(time.Minute) + + c.batchChan <- CollectedResource{ + ResourceType: ContainerCPUThrottleEvent, + Object: map[string]interface{}{ + "namespace": pod.Namespace, + "workload_name": workloadName, + "workload_kind": workloadKind, + "pod_name": pod.Name, + "container_name": containerMetrics.Name, + "cpu_usage_millicores": cpuUsageMillis, + "cpu_request_millicores": cpuRequestMillis, + "cpu_limit_millicores": cpuLimitMillis, + "throttled_fraction": throttleFraction, + "timestamp": roundedTS.Format(time.RFC3339Nano), + }, + Timestamp: now, + EventType: EventTypeAdd, + Key: fmt.Sprintf("cpu-throttle/%s/%s/%s", pod.Namespace, pod.Name, containerMetrics.Name), + } +} + // collectContainerGPUMetrics collects GPU metrics for a container using Prometheus queries func (c *ContainerResourceCollector) collectContainerGPUMetrics(ctx context.Context, pod *corev1.Pod, containerName string) (map[string]interface{}, error) { metrics := make(map[string]interface{}) diff --git a/internal/collector/interface.go b/internal/collector/interface.go index 088888eb..320eb0e2 100644 --- a/internal/collector/interface.go +++ b/internal/collector/interface.go @@ -149,6 +149,7 @@ const ( ContainerOOMEvent ContainerCrashLoopEvent ContainerStartupLifecycle + ContainerCPUThrottleEvent ) // String returns the string representation of the ResourceType @@ -212,6 +213,7 @@ func (r ResourceType) String() string { ContainerOOMEvent: "container_oom_event", ContainerCrashLoopEvent: "container_crashloop_event", ContainerStartupLifecycle: "container_startup_lifecycle", + ContainerCPUThrottleEvent: "container_cpu_throttle_event", } if name, ok := names[r]; ok { @@ -339,6 +341,8 @@ func (r ResourceType) ProtoType() gen.ResourceType { return gen.ResourceType_RESOURCE_TYPE_CONTAINER_CRASHLOOP_EVENT case ContainerStartupLifecycle: return gen.ResourceType_RESOURCE_TYPE_CONTAINER_STARTUP_LIFECYCLE + case ContainerCPUThrottleEvent: + return gen.ResourceType_RESOURCE_TYPE_CONTAINER_CPU_THROTTLE_EVENT default: return gen.ResourceType_RESOURCE_TYPE_UNSPECIFIED } diff --git a/proto/dakr_proto_descriptor.bin b/proto/dakr_proto_descriptor.bin index 693524b64bcc3f648f988d58e6f56d35f50edd48..372ed000dfd99eff401ee70fbf85e6561c58057c 100644 GIT binary patch delta 10484 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 L;3rMRzn%X7eK^ml delta 10399 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 Bz6k&T From 300d9dee154ac028dc4e16d54542bb8796b09a57 Mon Sep 17 00:00:00 2001 From: tzvonimir Date: Sun, 8 Mar 2026 00:32:09 +0100 Subject: [PATCH 3/3] Fix up lint issues --- internal/collector/pod_collector.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/internal/collector/pod_collector.go b/internal/collector/pod_collector.go index 67f4885c..e2475681 100644 --- a/internal/collector/pod_collector.go +++ b/internal/collector/pod_collector.go @@ -482,16 +482,16 @@ func (c *PodCollector) emitContainerOOMEvent(pod *corev1.Pod, status corev1.Cont ResourceType: ContainerOOMEvent, Object: map[string]interface{}{ "namespace": pod.Namespace, - "workload_name": workloadName, - "workload_kind": workloadKind, - "pod_name": pod.Name, - "container_name": status.Name, - "memory_usage_bytes": usageBytes, + "workload_name": workloadName, + "workload_kind": workloadKind, + "pod_name": pod.Name, + "container_name": status.Name, + "memory_usage_bytes": usageBytes, "memory_request_bytes": requestBytes, - "memory_limit_bytes": limitBytes, - "restart_count": status.RestartCount, - "exit_code": exitCode, - "timestamp": time.Now().Format(time.RFC3339Nano), + "memory_limit_bytes": limitBytes, + "restart_count": status.RestartCount, + "exit_code": exitCode, + "timestamp": time.Now().Format(time.RFC3339Nano), }, Timestamp: time.Now(), EventType: EventTypeAdd, @@ -522,7 +522,7 @@ func (c *PodCollector) emitContainerCrashLoopEvent(pod *corev1.Pod, status corev c.batchChan <- CollectedResource{ ResourceType: ContainerCrashLoopEvent, Object: map[string]interface{}{ - "namespace": pod.Namespace, + "namespace": pod.Namespace, "workload_name": workloadName, "workload_kind": workloadKind, "pod_name": pod.Name, @@ -540,7 +540,7 @@ func (c *PodCollector) emitContainerCrashLoopEvent(pod *corev1.Pod, status corev } // trackStartupLifecycle tracks container startup phase transitions and emits lifecycle events. -func (c *PodCollector) trackStartupLifecycle(oldPod, newPod *corev1.Pod) { +func (c *PodCollector) trackStartupLifecycle(_, newPod *corev1.Pod) { now := time.Now() // Periodically clean up stale entries