| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863 |
- package metrics
- import (
- "testing"
- "github.com/opencost/opencost/core/pkg/clustercache"
- "github.com/prometheus/client_golang/prometheus"
- dto "github.com/prometheus/client_model/go"
- v1 "k8s.io/api/core/v1"
- "k8s.io/apimachinery/pkg/api/resource"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/types"
- )
- func TestKubecostPodCollector_Describe(t *testing.T) {
- tests := []struct {
- name string
- disabledMetrics []string
- expectMetric bool
- }{
- {
- name: "annotations enabled",
- disabledMetrics: []string{},
- expectMetric: true,
- },
- {
- name: "annotations disabled",
- disabledMetrics: []string{"kube_pod_annotations"},
- expectMetric: false,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- mc := MetricsConfig{
- DisabledMetrics: tt.disabledMetrics,
- }
- kpc := KubecostPodCollector{
- KubeClusterCache: NewFakePodCache([]*clustercache.Pod{}),
- metricsConfig: mc,
- }
- ch := make(chan *prometheus.Desc, 10)
- kpc.Describe(ch)
- close(ch)
- count := 0
- for range ch {
- count++
- }
- if tt.expectMetric && count == 0 {
- t.Error("Expected metric description but got none")
- }
- if !tt.expectMetric && count > 0 {
- t.Error("Expected no metric description but got some")
- }
- })
- }
- }
- func TestKubecostPodCollector_Collect(t *testing.T) {
- tests := []struct {
- name string
- pods []*clustercache.Pod
- disabledMetrics []string
- expectedCount int
- }{
- {
- name: "pod with annotations",
- pods: []*clustercache.Pod{
- {
- UID: types.UID("pod-uid-1"),
- Name: "test-pod",
- Namespace: "default",
- Annotations: map[string]string{
- "prometheus.io/scrape": "true",
- "prometheus.io/port": "8080",
- },
- },
- },
- disabledMetrics: []string{},
- expectedCount: 1,
- },
- {
- name: "pod without annotations",
- pods: []*clustercache.Pod{
- {
- UID: types.UID("pod-uid-2"),
- Name: "empty-pod",
- Namespace: "default",
- Annotations: map[string]string{},
- },
- },
- disabledMetrics: []string{},
- expectedCount: 0,
- },
- {
- name: "multiple pods with mixed annotations",
- pods: []*clustercache.Pod{
- {
- UID: types.UID("pod-uid-3"),
- Name: "pod1",
- Namespace: "ns1",
- Annotations: map[string]string{"key": "value"},
- },
- {
- UID: types.UID("pod-uid-4"),
- Name: "pod2",
- Namespace: "ns1",
- Annotations: map[string]string{},
- },
- },
- disabledMetrics: []string{},
- expectedCount: 1,
- },
- {
- name: "metric disabled",
- pods: []*clustercache.Pod{
- {
- UID: types.UID("pod-uid-5"),
- Name: "test-pod",
- Namespace: "default",
- Annotations: map[string]string{"test": "annotation"},
- },
- },
- disabledMetrics: []string{"kube_pod_annotations"},
- expectedCount: 0,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- mc := MetricsConfig{
- DisabledMetrics: tt.disabledMetrics,
- }
- kpc := KubecostPodCollector{
- KubeClusterCache: NewFakePodCache(tt.pods),
- metricsConfig: mc,
- }
- ch := make(chan prometheus.Metric, 10)
- kpc.Collect(ch)
- close(ch)
- count := 0
- for range ch {
- count++
- }
- if count != tt.expectedCount {
- t.Errorf("Expected %d metrics, got %d", tt.expectedCount, count)
- }
- })
- }
- }
- func TestPodAnnotationMetric(t *testing.T) {
- labelNames := []string{"annotation_key1", "annotation_key2"}
- labelValues := []string{"value1", "value2"}
- metric := newPodAnnotationMetric("kube_pod_annotations", "test-ns", "test-pod", "test-uid", labelNames, labelValues)
- // Test Desc method
- desc := metric.Desc()
- if desc == nil {
- t.Error("Expected non-nil descriptor")
- }
- // Test Write method
- var dtoMetric dto.Metric
- err := metric.Write(&dtoMetric)
- if err != nil {
- t.Errorf("Expected no error, got %v", err)
- }
- if dtoMetric.Gauge == nil {
- t.Error("Expected gauge metric")
- }
- if *dtoMetric.Gauge.Value != 1.0 {
- t.Errorf("Expected gauge value 1.0, got %f", *dtoMetric.Gauge.Value)
- }
- // Verify labels
- expectedLabels := map[string]string{
- "annotation_key1": "value1",
- "annotation_key2": "value2",
- "namespace": "test-ns",
- "pod": "test-pod",
- "uid": "test-uid",
- }
- actualLabels := make(map[string]string)
- for _, label := range dtoMetric.Label {
- actualLabels[*label.Name] = *label.Value
- }
- for key, expectedValue := range expectedLabels {
- if actualValue, ok := actualLabels[key]; !ok {
- t.Errorf("Missing label %s", key)
- } else if actualValue != expectedValue {
- t.Errorf("Label %s: expected %s, got %s", key, expectedValue, actualValue)
- }
- }
- }
- func TestKubePodCollector_Describe(t *testing.T) {
- tests := []struct {
- name string
- disabledMetrics []string
- expectedCount int
- }{
- {
- name: "all metrics enabled",
- disabledMetrics: []string{},
- expectedCount: 10,
- },
- {
- name: "some metrics disabled",
- disabledMetrics: []string{
- "kube_pod_labels",
- "kube_pod_owner",
- "kube_pod_container_status_running",
- },
- expectedCount: 7,
- },
- {
- name: "all metrics disabled",
- disabledMetrics: []string{
- "kube_pod_labels",
- "kube_pod_owner",
- "kube_pod_container_status_running",
- "kube_pod_container_status_terminated_reason",
- "kube_pod_container_status_restarts_total",
- "kube_pod_container_resource_requests",
- "kube_pod_container_resource_limits",
- "kube_pod_container_resource_limits_cpu_cores",
- "kube_pod_container_resource_limits_memory_bytes",
- "kube_pod_status_phase",
- },
- expectedCount: 0,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- mc := MetricsConfig{
- DisabledMetrics: tt.disabledMetrics,
- }
- kpc := KubePodCollector{
- KubeClusterCache: NewFakePodCache([]*clustercache.Pod{}),
- metricsConfig: mc,
- }
- ch := make(chan *prometheus.Desc, 15)
- kpc.Describe(ch)
- close(ch)
- count := 0
- for range ch {
- count++
- }
- if count != tt.expectedCount {
- t.Errorf("Expected %d metrics, got %d", tt.expectedCount, count)
- }
- })
- }
- }
- func TestKubePodCollector_Collect(t *testing.T) {
- boolTrue := true
- tests := []struct {
- name string
- pods []*clustercache.Pod
- disabledMetrics []string
- expectedCount int
- }{
- {
- name: "pod with all features",
- pods: []*clustercache.Pod{
- {
- UID: types.UID("pod-uid-1"),
- Name: "test-pod",
- Namespace: "default",
- Labels: map[string]string{
- "app": "test",
- "version": "v1",
- },
- OwnerReferences: []metav1.OwnerReference{
- {
- Name: "test-deployment",
- Kind: "Deployment",
- Controller: &boolTrue,
- },
- },
- Status: clustercache.PodStatus{
- Phase: v1.PodRunning,
- ContainerStatuses: []v1.ContainerStatus{
- {
- Name: "container1",
- RestartCount: 2,
- State: v1.ContainerState{
- Running: &v1.ContainerStateRunning{},
- },
- },
- },
- },
- Spec: clustercache.PodSpec{
- NodeName: "node1",
- Containers: []clustercache.Container{
- {
- Name: "container1",
- Resources: v1.ResourceRequirements{
- Requests: v1.ResourceList{
- v1.ResourceCPU: resource.MustParse("100m"),
- v1.ResourceMemory: resource.MustParse("128Mi"),
- },
- Limits: v1.ResourceList{
- v1.ResourceCPU: resource.MustParse("200m"),
- v1.ResourceMemory: resource.MustParse("256Mi"),
- },
- },
- },
- },
- },
- },
- },
- disabledMetrics: []string{},
- expectedCount: 15, // 5 phases + 1 labels + 1 owner + 1 restarts + 1 running + 2 requests + 4 limits
- },
- {
- name: "pod without containers",
- pods: []*clustercache.Pod{
- {
- UID: types.UID("pod-uid-2"),
- Name: "empty-pod",
- Namespace: "default",
- Labels: map[string]string{"test": "label"},
- Status: clustercache.PodStatus{
- Phase: v1.PodPending,
- },
- Spec: clustercache.PodSpec{
- Containers: []clustercache.Container{},
- },
- },
- },
- disabledMetrics: []string{},
- expectedCount: 6, // 5 phases + 1 labels
- },
- {
- name: "pod with terminated container",
- pods: []*clustercache.Pod{
- {
- UID: types.UID("pod-uid-3"),
- Name: "terminated-pod",
- Namespace: "default",
- Labels: map[string]string{},
- Status: clustercache.PodStatus{
- Phase: v1.PodFailed,
- ContainerStatuses: []v1.ContainerStatus{
- {
- Name: "failed-container",
- RestartCount: 5,
- State: v1.ContainerState{
- Terminated: &v1.ContainerStateTerminated{
- Reason: "OOMKilled",
- },
- },
- },
- },
- },
- Spec: clustercache.PodSpec{
- Containers: []clustercache.Container{
- {
- Name: "failed-container",
- Resources: v1.ResourceRequirements{},
- },
- },
- },
- },
- },
- disabledMetrics: []string{},
- expectedCount: 8, // 5 phases + 1 labels + 1 restarts + 1 terminated reason
- },
- {
- name: "pod without phase",
- pods: []*clustercache.Pod{
- {
- UID: types.UID("pod-uid-4"),
- Name: "no-phase-pod",
- Namespace: "default",
- Labels: map[string]string{"app": "test"},
- Status: clustercache.PodStatus{
- Phase: "", // Empty phase
- },
- Spec: clustercache.PodSpec{},
- },
- },
- disabledMetrics: []string{},
- expectedCount: 1, // Only labels
- },
- {
- name: "multiple containers",
- pods: []*clustercache.Pod{
- {
- UID: types.UID("pod-uid-5"),
- Name: "multi-container-pod",
- Namespace: "default",
- Labels: map[string]string{},
- Status: clustercache.PodStatus{
- Phase: v1.PodRunning,
- ContainerStatuses: []v1.ContainerStatus{
- {
- Name: "container1",
- RestartCount: 0,
- State: v1.ContainerState{
- Running: &v1.ContainerStateRunning{},
- },
- },
- {
- Name: "container2",
- RestartCount: 1,
- State: v1.ContainerState{
- Running: &v1.ContainerStateRunning{},
- },
- },
- },
- },
- Spec: clustercache.PodSpec{
- NodeName: "node2",
- Containers: []clustercache.Container{
- {
- Name: "container1",
- Resources: v1.ResourceRequirements{
- Requests: v1.ResourceList{
- v1.ResourceCPU: resource.MustParse("50m"),
- },
- Limits: v1.ResourceList{
- v1.ResourceCPU: resource.MustParse("100m"),
- },
- },
- },
- {
- Name: "container2",
- Resources: v1.ResourceRequirements{
- Requests: v1.ResourceList{
- v1.ResourceMemory: resource.MustParse("64Mi"),
- },
- Limits: v1.ResourceList{
- v1.ResourceMemory: resource.MustParse("128Mi"),
- },
- },
- },
- },
- },
- },
- },
- disabledMetrics: []string{},
- expectedCount: 16, // 5 phases + 1 labels + 2 restarts + 2 running + 2 requests + 4 limits
- },
- {
- name: "metrics disabled",
- pods: []*clustercache.Pod{
- {
- UID: types.UID("pod-uid-6"),
- Name: "test-pod",
- Namespace: "default",
- Labels: map[string]string{"app": "test"},
- Status: clustercache.PodStatus{
- Phase: v1.PodRunning,
- },
- Spec: clustercache.PodSpec{},
- },
- },
- disabledMetrics: []string{"kube_pod_labels", "kube_pod_status_phase"},
- expectedCount: 0,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- mc := MetricsConfig{
- DisabledMetrics: tt.disabledMetrics,
- }
- kpc := KubePodCollector{
- KubeClusterCache: NewFakePodCache(tt.pods),
- metricsConfig: mc,
- }
- ch := make(chan prometheus.Metric, 30)
- kpc.Collect(ch)
- close(ch)
- count := 0
- for range ch {
- count++
- }
- if count != tt.expectedCount {
- t.Errorf("Expected %d metrics, got %d", tt.expectedCount, count)
- }
- })
- }
- }
- func TestKubePodLabelsMetric(t *testing.T) {
- labelNames := []string{"label_app", "label_env"}
- labelValues := []string{"webapp", "production"}
- metric := newKubePodLabelsMetric("kube_pod_labels", "prod", "web-pod", "pod-uid", labelNames, labelValues)
- // Test Desc method
- desc := metric.Desc()
- if desc == nil {
- t.Error("Expected non-nil descriptor")
- }
- // Test Write method
- var dtoMetric dto.Metric
- err := metric.Write(&dtoMetric)
- if err != nil {
- t.Errorf("Expected no error, got %v", err)
- }
- if dtoMetric.Gauge == nil {
- t.Error("Expected gauge metric")
- }
- if *dtoMetric.Gauge.Value != 1.0 {
- t.Errorf("Expected gauge value 1.0, got %f", *dtoMetric.Gauge.Value)
- }
- // Verify labels
- expectedLabels := map[string]string{
- "label_app": "webapp",
- "label_env": "production",
- "namespace": "prod",
- "pod": "web-pod",
- "uid": "pod-uid",
- }
- actualLabels := make(map[string]string)
- for _, label := range dtoMetric.Label {
- actualLabels[*label.Name] = *label.Value
- }
- for key, expectedValue := range expectedLabels {
- if actualValue, ok := actualLabels[key]; !ok {
- t.Errorf("Missing label %s", key)
- } else if actualValue != expectedValue {
- t.Errorf("Label %s: expected %s, got %s", key, expectedValue, actualValue)
- }
- }
- }
- func TestKubePodContainerStatusRestartsTotalMetric(t *testing.T) {
- metric := newKubePodContainerStatusRestartsTotalMetric("kube_pod_container_status_restarts_total", "default", "test-pod", "pod-uid", "app-container", 3.0)
- // Test Desc method
- desc := metric.Desc()
- if desc == nil {
- t.Error("Expected non-nil descriptor")
- }
- // Test Write method
- var dtoMetric dto.Metric
- err := metric.Write(&dtoMetric)
- if err != nil {
- t.Errorf("Expected no error, got %v", err)
- }
- if dtoMetric.Counter == nil {
- t.Error("Expected counter metric")
- }
- if *dtoMetric.Counter.Value != 3.0 {
- t.Errorf("Expected counter value 3.0, got %f", *dtoMetric.Counter.Value)
- }
- }
- func TestKubePodContainerStatusTerminatedReasonMetric(t *testing.T) {
- metric := newKubePodContainerStatusTerminatedReasonMetric("kube_pod_container_status_terminated_reason", "default", "crashed-pod", "pod-uid", "failing-container", "Error")
- var dtoMetric dto.Metric
- err := metric.Write(&dtoMetric)
- if err != nil {
- t.Errorf("Expected no error, got %v", err)
- }
- if dtoMetric.Gauge == nil {
- t.Error("Expected gauge metric")
- }
- if *dtoMetric.Gauge.Value != 1.0 {
- t.Errorf("Expected gauge value 1.0, got %f", *dtoMetric.Gauge.Value)
- }
- // Check for reason label
- hasReason := false
- for _, label := range dtoMetric.Label {
- if *label.Name == "reason" && *label.Value == "Error" {
- hasReason = true
- break
- }
- }
- if !hasReason {
- t.Error("Expected reason label with value 'Error'")
- }
- }
- func TestKubePodStatusPhaseMetric(t *testing.T) {
- metric := newKubePodStatusPhaseMetric("kube_pod_status_phase", "default", "test-pod", "pod-uid", "Running", 1.0)
- var dtoMetric dto.Metric
- err := metric.Write(&dtoMetric)
- if err != nil {
- t.Errorf("Expected no error, got %v", err)
- }
- if dtoMetric.Gauge == nil {
- t.Error("Expected gauge metric")
- }
- // Check phase label
- hasPhase := false
- for _, label := range dtoMetric.Label {
- if *label.Name == "phase" && *label.Value == "Running" {
- hasPhase = true
- break
- }
- }
- if !hasPhase {
- t.Error("Expected phase label with value 'Running'")
- }
- }
- func TestKubePodContainerStatusRunningMetric(t *testing.T) {
- metric := newKubePodContainerStatusRunningMetric("kube_pod_container_status_running", "default", "running-pod", "pod-uid", "web-container")
- var dtoMetric dto.Metric
- err := metric.Write(&dtoMetric)
- if err != nil {
- t.Errorf("Expected no error, got %v", err)
- }
- if dtoMetric.Gauge == nil {
- t.Error("Expected gauge metric")
- }
- if *dtoMetric.Gauge.Value != 1.0 {
- t.Errorf("Expected gauge value 1.0, got %f", *dtoMetric.Gauge.Value)
- }
- }
- func TestKubePodContainerResourceRequestsMetric(t *testing.T) {
- metric := newKubePodContainerResourceRequestsMetric("kube_pod_container_resource_requests", "default", "test-pod", "pod-uid", "container1", "node1", "cpu", "core", 0.1)
- var dtoMetric dto.Metric
- err := metric.Write(&dtoMetric)
- if err != nil {
- t.Errorf("Expected no error, got %v", err)
- }
- if dtoMetric.Gauge == nil {
- t.Error("Expected gauge metric")
- }
- if *dtoMetric.Gauge.Value != 0.1 {
- t.Errorf("Expected gauge value 0.1, got %f", *dtoMetric.Gauge.Value)
- }
- // Verify all labels
- expectedLabels := map[string]string{
- "namespace": "default",
- "pod": "test-pod",
- "container": "container1",
- "uid": "pod-uid",
- "node": "node1",
- "resource": "cpu",
- "unit": "core",
- }
- actualLabels := make(map[string]string)
- for _, label := range dtoMetric.Label {
- actualLabels[*label.Name] = *label.Value
- }
- for key, expectedValue := range expectedLabels {
- if actualValue, ok := actualLabels[key]; !ok {
- t.Errorf("Missing label %s", key)
- } else if actualValue != expectedValue {
- t.Errorf("Label %s: expected %s, got %s", key, expectedValue, actualValue)
- }
- }
- }
- func TestKubePodContainerResourceLimitsMetric(t *testing.T) {
- metric := newKubePodContainerResourceLimitsMetric("kube_pod_container_resource_limits", "default", "test-pod", "pod-uid", "container1", "node1", "memory", "byte", 268435456)
- var dtoMetric dto.Metric
- err := metric.Write(&dtoMetric)
- if err != nil {
- t.Errorf("Expected no error, got %v", err)
- }
- if dtoMetric.Gauge == nil {
- t.Error("Expected gauge metric")
- }
- if *dtoMetric.Gauge.Value != 268435456 {
- t.Errorf("Expected gauge value 268435456, got %f", *dtoMetric.Gauge.Value)
- }
- }
- func TestKubePodContainerResourceLimitsCPUCoresMetric(t *testing.T) {
- metric := newKubePodContainerResourceLimitsCPUCoresMetric("kube_pod_container_resource_limits_cpu_cores", "default", "test-pod", "pod-uid", "container1", "node1", 2.0)
- var dtoMetric dto.Metric
- err := metric.Write(&dtoMetric)
- if err != nil {
- t.Errorf("Expected no error, got %v", err)
- }
- if dtoMetric.Gauge == nil {
- t.Error("Expected gauge metric")
- }
- if *dtoMetric.Gauge.Value != 2.0 {
- t.Errorf("Expected gauge value 2.0, got %f", *dtoMetric.Gauge.Value)
- }
- }
- func TestKubePodContainerResourceLimitsMemoryBytesMetric(t *testing.T) {
- metric := newKubePodContainerResourceLimitsMemoryBytesMetric("kube_pod_container_resource_limits_memory_bytes", "default", "test-pod", "pod-uid", "container1", "node1", 536870912)
- var dtoMetric dto.Metric
- err := metric.Write(&dtoMetric)
- if err != nil {
- t.Errorf("Expected no error, got %v", err)
- }
- if dtoMetric.Gauge == nil {
- t.Error("Expected gauge metric")
- }
- if *dtoMetric.Gauge.Value != 536870912 {
- t.Errorf("Expected gauge value 536870912, got %f", *dtoMetric.Gauge.Value)
- }
- }
- func TestKubePodOwnerMetric(t *testing.T) {
- metric := newKubePodOwnerMetric("kube_pod_owner", "default", "test-pod", "test-uid", "test-replicaset", "ReplicaSet", true)
- var dtoMetric dto.Metric
- err := metric.Write(&dtoMetric)
- if err != nil {
- t.Errorf("Expected no error, got %v", err)
- }
- if dtoMetric.Gauge == nil {
- t.Error("Expected gauge metric")
- }
- if *dtoMetric.Gauge.Value != 1.0 {
- t.Errorf("Expected gauge value 1.0, got %f", *dtoMetric.Gauge.Value)
- }
- // Verify owner-specific labels
- expectedLabels := map[string]string{
- "namespace": "default",
- "pod": "test-pod",
- "uid": "test-uid",
- "owner_name": "test-replicaset",
- "owner_kind": "ReplicaSet",
- "owner_is_controller": "true",
- }
- actualLabels := make(map[string]string)
- for _, label := range dtoMetric.Label {
- actualLabels[*label.Name] = *label.Value
- }
- for key, expectedValue := range expectedLabels {
- if actualValue, ok := actualLabels[key]; !ok {
- t.Errorf("Missing label %s", key)
- } else if actualValue != expectedValue {
- t.Errorf("Label %s: expected %s, got %s", key, expectedValue, actualValue)
- }
- }
- }
- func TestPodPhaseMetrics(t *testing.T) {
- // Test that all pod phases generate correct metrics
- pod := &clustercache.Pod{
- UID: types.UID("phase-test-uid"),
- Name: "phase-test-pod",
- Namespace: "default",
- Labels: map[string]string{},
- Status: clustercache.PodStatus{
- Phase: v1.PodRunning,
- },
- Spec: clustercache.PodSpec{},
- }
- mc := MetricsConfig{
- DisabledMetrics: []string{"kube_pod_labels"}, // Only test phase metrics
- }
- kpc := KubePodCollector{
- KubeClusterCache: NewFakePodCache([]*clustercache.Pod{pod}),
- metricsConfig: mc,
- }
- ch := make(chan prometheus.Metric, 10)
- kpc.Collect(ch)
- close(ch)
- phaseMetrics := make(map[string]float64)
- for metric := range ch {
- var dtoMetric dto.Metric
- metric.Write(&dtoMetric)
- for _, label := range dtoMetric.Label {
- if *label.Name == "phase" {
- phaseMetrics[*label.Value] = *dtoMetric.Gauge.Value
- }
- }
- }
- // Verify all phases are emitted
- expectedPhases := map[string]float64{
- "Pending": 0.0,
- "Succeeded": 0.0,
- "Failed": 0.0,
- "Unknown": 0.0,
- "Running": 1.0, // Only Running should be 1
- }
- for phase, expectedValue := range expectedPhases {
- if actualValue, ok := phaseMetrics[phase]; !ok {
- t.Errorf("Missing phase metric for %s", phase)
- } else if actualValue != expectedValue {
- t.Errorf("Phase %s: expected value %f, got %f", phase, expectedValue, actualValue)
- }
- }
- }
- // FakePodCache implements ClusterCache interface for testing
- type FakePodCache struct {
- clustercache.ClusterCache
- pods []*clustercache.Pod
- }
- func (f FakePodCache) GetAllPods() []*clustercache.Pod {
- return f.pods
- }
- func NewFakePodCache(pods []*clustercache.Pod) FakePodCache {
- return FakePodCache{
- pods: pods,
- }
- }
|