Skip to content

Commit 1071531

Browse files
Copilotrejain456
andcommitted
Fix CNS IP demand overcounting by filtering terminal Pods in IPAMv2
Co-authored-by: rejain456 <[email protected]>
1 parent 18dbd62 commit 1071531

File tree

2 files changed

+109
-1
lines changed

2 files changed

+109
-1
lines changed

cns/ipampool/v2/adapter.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@ func (m *adapter) GetStateSnapshot() cns.IpamPoolMonitorStateSnapshot {
3131

3232
func PodIPDemandListener(ch chan<- int) func([]v1.Pod) {
3333
return func(pods []v1.Pod) {
34-
ch <- len(pods)
34+
// Filter out Pods in terminal phases (Succeeded/Failed) since they no longer
35+
// have network sandboxes and don't contribute to IP demand
36+
activePods := 0
37+
for _, pod := range pods {
38+
if pod.Status.Phase != v1.PodSucceeded && pod.Status.Phase != v1.PodFailed {
39+
activePods++
40+
}
41+
}
42+
ch <- activePods
3543
}
3644
}

cns/ipampool/v2/adapter_test.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package v2
2+
3+
import (
4+
"testing"
5+
6+
v1 "k8s.io/api/core/v1"
7+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
8+
)
9+
10+
func TestPodIPDemandListener(t *testing.T) {
11+
tests := []struct {
12+
name string
13+
pods []v1.Pod
14+
expected int
15+
}{
16+
{
17+
name: "empty pod list",
18+
pods: []v1.Pod{},
19+
expected: 0,
20+
},
21+
{
22+
name: "single running pod",
23+
pods: []v1.Pod{
24+
{
25+
ObjectMeta: metav1.ObjectMeta{Name: "pod1"},
26+
Status: v1.PodStatus{Phase: v1.PodRunning},
27+
},
28+
},
29+
expected: 1,
30+
},
31+
{
32+
name: "multiple running pods",
33+
pods: []v1.Pod{
34+
{
35+
ObjectMeta: metav1.ObjectMeta{Name: "pod1"},
36+
Status: v1.PodStatus{Phase: v1.PodRunning},
37+
},
38+
{
39+
ObjectMeta: metav1.ObjectMeta{Name: "pod2"},
40+
Status: v1.PodStatus{Phase: v1.PodPending},
41+
},
42+
},
43+
expected: 2,
44+
},
45+
{
46+
name: "mix of running and terminal pods - should exclude terminal",
47+
pods: []v1.Pod{
48+
{
49+
ObjectMeta: metav1.ObjectMeta{Name: "pod1"},
50+
Status: v1.PodStatus{Phase: v1.PodRunning},
51+
},
52+
{
53+
ObjectMeta: metav1.ObjectMeta{Name: "pod2"},
54+
Status: v1.PodStatus{Phase: v1.PodSucceeded},
55+
},
56+
{
57+
ObjectMeta: metav1.ObjectMeta{Name: "pod3"},
58+
Status: v1.PodStatus{Phase: v1.PodFailed},
59+
},
60+
{
61+
ObjectMeta: metav1.ObjectMeta{Name: "pod4"},
62+
Status: v1.PodStatus{Phase: v1.PodPending},
63+
},
64+
},
65+
expected: 2, // Only pod1 (Running) and pod4 (Pending) should be counted
66+
},
67+
{
68+
name: "only terminal pods - should count zero",
69+
pods: []v1.Pod{
70+
{
71+
ObjectMeta: metav1.ObjectMeta{Name: "pod1"},
72+
Status: v1.PodStatus{Phase: v1.PodSucceeded},
73+
},
74+
{
75+
ObjectMeta: metav1.ObjectMeta{Name: "pod2"},
76+
Status: v1.PodStatus{Phase: v1.PodFailed},
77+
},
78+
},
79+
expected: 0,
80+
},
81+
}
82+
83+
for _, tt := range tests {
84+
t.Run(tt.name, func(t *testing.T) {
85+
ch := make(chan int, 1)
86+
listener := PodIPDemandListener(ch)
87+
88+
listener(tt.pods)
89+
90+
select {
91+
case result := <-ch:
92+
if result != tt.expected {
93+
t.Errorf("expected %d, got %d", tt.expected, result)
94+
}
95+
default:
96+
t.Error("expected value in channel")
97+
}
98+
})
99+
}
100+
}

0 commit comments

Comments
 (0)