Skip to content

Commit b81925d

Browse files
committed
Fix issue where PolicyEndpoints are not updated correctly when Pods are in a terminal phase (Succeeded or Failed).
1 parent c0ac208 commit b81925d

File tree

5 files changed

+145
-5
lines changed

5 files changed

+145
-5
lines changed

internal/eventhandlers/pod.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ func (h *enqueueRequestForPodEvent) Update(ctx context.Context, e event.UpdateEv
6565
if equality.Semantic.DeepEqual(podOld.Annotations, podNew.Annotations) &&
6666
equality.Semantic.DeepEqual(podOld.Labels, podNew.Labels) &&
6767
equality.Semantic.DeepEqual(podOld.DeletionTimestamp.IsZero(), podNew.DeletionTimestamp.IsZero()) &&
68-
equality.Semantic.DeepEqual(podOld.Status.PodIP, podNew.Status.PodIP) {
68+
equality.Semantic.DeepEqual(podOld.Status.PodIP, podNew.Status.PodIP) &&
69+
equality.Semantic.DeepEqual(podOld.Status.Phase, podNew.Status.Phase) {
6970
return
7071
}
7172
h.enqueueReferredPolicies(ctx, q, podNew, podOld)

pkg/k8s/pod_utils.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ func GetPodIP(pod *corev1.Pod) string {
2020
}
2121
}
2222

23+
// IsPodNetworkReady returns true if the pod has at least 1 IP address and is in a running state
24+
func IsPodNetworkReady(pod *corev1.Pod) bool {
25+
return len(GetPodIP(pod)) > 0 &&
26+
pod.Status.Phase != corev1.PodSucceeded &&
27+
pod.Status.Phase != corev1.PodFailed
28+
}
29+
2330
// LookupContainerPortAndName returns numerical containerPort and portName for specific port and protocol
2431
func LookupContainerPortAndName(pod *corev1.Pod, port intstr.IntOrString, protocol corev1.Protocol) (int32, string, error) {
2532
for _, podContainer := range pod.Spec.Containers {

pkg/k8s/pod_utils_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,3 +185,57 @@ func Test_LookupContainerPortAndName(t *testing.T) {
185185
})
186186
}
187187
}
188+
func TestIsPodNetworkReady(t *testing.T) {
189+
tests := []struct {
190+
name string
191+
pod *corev1.Pod
192+
expected bool
193+
}{
194+
{
195+
name: "running pod with IP",
196+
pod: &corev1.Pod{
197+
Status: corev1.PodStatus{
198+
PodIP: "10.0.0.1",
199+
Phase: corev1.PodRunning,
200+
},
201+
},
202+
expected: true,
203+
},
204+
{
205+
name: "succeeded pod with IP should be excluded",
206+
pod: &corev1.Pod{
207+
Status: corev1.PodStatus{
208+
PodIP: "10.0.0.1",
209+
Phase: corev1.PodSucceeded,
210+
},
211+
},
212+
expected: false,
213+
},
214+
{
215+
name: "failed pod with IP should be excluded",
216+
pod: &corev1.Pod{
217+
Status: corev1.PodStatus{
218+
PodIP: "10.0.0.1",
219+
Phase: corev1.PodFailed,
220+
},
221+
},
222+
expected: false,
223+
},
224+
{
225+
name: "running pod without IP",
226+
pod: &corev1.Pod{
227+
Status: corev1.PodStatus{
228+
Phase: corev1.PodRunning,
229+
},
230+
},
231+
expected: false,
232+
},
233+
}
234+
235+
for _, tt := range tests {
236+
t.Run(tt.name, func(t *testing.T) {
237+
result := IsPodNetworkReady(tt.pod)
238+
assert.Equal(t, tt.expected, result)
239+
})
240+
}
241+
}

pkg/resolvers/endpoints.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,8 @@ func (r *defaultEndpointsResolver) computePodSelectorEndpoints(ctx context.Conte
116116
return nil, err
117117
}
118118
for _, pod := range podList.Items {
119-
podIP := k8s.GetPodIP(&pod)
120-
if len(podIP) > 0 {
119+
if k8s.IsPodNetworkReady(&pod) {
120+
podIP := k8s.GetPodIP(&pod)
121121
podEndpoints = append(podEndpoints, policyinfo.PodEndpoint{
122122
PodIP: policyinfo.NetworkAddress(podIP),
123123
HostIP: policyinfo.NetworkAddress(pod.Status.HostIP),
@@ -224,6 +224,9 @@ func (r *defaultEndpointsResolver) getIngressRulesPorts(ctx context.Context, pol
224224
r.logger.V(2).Info("list pods for ingress", "podList", *podList, "namespace", policyNamespace, "selector", *policyPodSelector)
225225
var portList []policyinfo.Port
226226
for _, pod := range podList.Items {
227+
if !k8s.IsPodNetworkReady(&pod) {
228+
continue
229+
}
227230
portList = append(portList, r.getPortList(pod, ports)...)
228231
r.logger.V(1).Info("Got ingress port from pod", "pod", types.NamespacedName{Namespace: pod.Namespace, Name: pod.Name}.String())
229232
}
@@ -349,10 +352,10 @@ func (r *defaultEndpointsResolver) getMatchingPodAddresses(ctx context.Context,
349352
r.logger.V(1).Info("Got pods for label selector", "count", len(podList.Items), "selector", ls.String())
350353

351354
for _, pod := range podList.Items {
352-
podIP := k8s.GetPodIP(&pod)
353-
if len(podIP) == 0 {
355+
if !k8s.IsPodNetworkReady(&pod) {
354356
continue
355357
}
358+
podIP := k8s.GetPodIP(&pod)
356359

357360
if policyType == networking.PolicyTypeEgress {
358361
portList = r.getPortList(pod, rulePorts)

pkg/resolvers/endpoints_test.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1101,3 +1101,78 @@ func TestEndpointsResolver_ResolveNetworkPeers_NamedIngressPortsIPBlocks(t *test
11011101
assert.Equal(t, portsMap[policy.Spec.Ingress[0].Ports[1].Port.StrVal], *ingPE.Ports[1].Port)
11021102
}
11031103
}
1104+
1105+
func TestEndpointsResolver_ExcludesTerminalPods(t *testing.T) {
1106+
ctrl := gomock.NewController(t)
1107+
defer ctrl.Finish()
1108+
1109+
mockClient := mock_client.NewMockClient(ctrl)
1110+
resolver := NewEndpointsResolver(mockClient, logr.New(&log.NullLogSink{}))
1111+
1112+
// Create pods in different phases
1113+
runningPod := &corev1.Pod{
1114+
ObjectMeta: metav1.ObjectMeta{
1115+
Name: "running-pod",
1116+
Namespace: "test-ns",
1117+
Labels: map[string]string{"app": "test"},
1118+
},
1119+
Status: corev1.PodStatus{
1120+
PodIP: "10.0.0.1",
1121+
Phase: corev1.PodRunning,
1122+
},
1123+
}
1124+
1125+
succeededPod := &corev1.Pod{
1126+
ObjectMeta: metav1.ObjectMeta{
1127+
Name: "succeeded-pod",
1128+
Namespace: "test-ns",
1129+
Labels: map[string]string{"app": "test"},
1130+
},
1131+
Status: corev1.PodStatus{
1132+
PodIP: "10.0.0.2",
1133+
Phase: corev1.PodSucceeded,
1134+
},
1135+
}
1136+
1137+
failedPod := &corev1.Pod{
1138+
ObjectMeta: metav1.ObjectMeta{
1139+
Name: "failed-pod",
1140+
Namespace: "test-ns",
1141+
Labels: map[string]string{"app": "test"},
1142+
},
1143+
Status: corev1.PodStatus{
1144+
PodIP: "10.0.0.3",
1145+
Phase: corev1.PodFailed,
1146+
},
1147+
}
1148+
1149+
podList := &corev1.PodList{
1150+
Items: []corev1.Pod{*runningPod, *succeededPod, *failedPod},
1151+
}
1152+
1153+
// Mock the List call for pod selector endpoints
1154+
mockClient.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any()).
1155+
DoAndReturn(func(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error {
1156+
list.(*corev1.PodList).Items = podList.Items
1157+
return nil
1158+
})
1159+
1160+
policy := &networking.NetworkPolicy{
1161+
ObjectMeta: metav1.ObjectMeta{
1162+
Name: "test-policy",
1163+
Namespace: "test-ns",
1164+
},
1165+
Spec: networking.NetworkPolicySpec{
1166+
PodSelector: metav1.LabelSelector{
1167+
MatchLabels: map[string]string{"app": "test"},
1168+
},
1169+
},
1170+
}
1171+
1172+
_, _, podEndpoints, err := resolver.Resolve(context.Background(), policy)
1173+
1174+
assert.NoError(t, err)
1175+
assert.Len(t, podEndpoints, 1, "Should only include running pod in PolicyEndpoints")
1176+
assert.Equal(t, "10.0.0.1", string(podEndpoints[0].PodIP))
1177+
assert.Equal(t, "running-pod", podEndpoints[0].Name)
1178+
}

0 commit comments

Comments
 (0)