pkg/health/eviction_test.go
Rama main · réplica de lectura del repositorio original.
package health
import (
"reflect"
"testing"
"time"
corev1 "k8s.io/api/core/v1"
)
// Messages are the kubelet's own formats (pkg/kubelet/eviction/helpers.go and
// the eviction manager's admission rejection).
func TestParsePodEviction(t *testing.T) {
cases := []struct {
name string
message string
want PodEviction
pressure string
}{
{
name: "disk pressure, current format",
message: "The node was low on resource: ephemeral-storage. Threshold quantity: 4013935590, available: 3812960Ki. Container app was using 2380Ki, request is 0, has larger consumption of ephemeral-storage. ",
want: PodEviction{Kind: EvictionNodePressure, Resource: "ephemeral-storage", Threshold: "4013935590", Available: "3812960Ki",
Containers: []EvictedContainer{{Name: "app", Usage: "2380Ki", Request: "0"}}},
pressure: "DiskPressure",
},
{
name: "memory pressure, two containers, fractional request",
message: "The node was low on resource: memory. Threshold quantity: 100Mi, available: 91844Ki. Container app was using 1.5Gi, request is 512Mi, has larger consumption of memory. Container sidecar was using 80Mi, request is 0, has larger consumption of memory. ",
want: PodEviction{Kind: EvictionNodePressure, Resource: "memory", Threshold: "100Mi", Available: "91844Ki",
Containers: []EvictedContainer{{Name: "app", Usage: "1.5Gi", Request: "512Mi"}, {Name: "sidecar", Usage: "80Mi", Request: "0"}}},
pressure: "MemoryPressure",
},
{
name: "legacy container format, no threshold",
message: "The node was low on resource: ephemeral-storage. Container writer was using 12Gi, which exceeds its request of 0. ",
want: PodEviction{Kind: EvictionNodePressure, Resource: "ephemeral-storage", Containers: []EvictedContainer{{Name: "writer", Usage: "12Gi", Request: "0"}}},
pressure: "DiskPressure",
},
{
name: "inodes",
message: "The node was low on resource: inodes. Threshold quantity: 5%, available: 1024. ",
want: PodEviction{Kind: EvictionNodePressure, Resource: "inodes", Threshold: "5%", Available: "1024"},
pressure: "DiskPressure",
},
{
name: "pids",
message: "The node was low on resource: pids. Threshold quantity: 3276, available: 1200. ",
want: PodEviction{Kind: EvictionNodePressure, Resource: "pids", Threshold: "3276", Available: "1200"},
pressure: "PIDPressure",
},
{
name: "admission rejection",
message: "The node had condition: [DiskPressure]. ",
want: PodEviction{Kind: EvictionNodeCondition, Resource: "ephemeral-storage", Conditions: []string{"DiskPressure"}},
pressure: "DiskPressure",
},
{
name: "container ephemeral-storage limit",
message: `Pod ephemeral local storage usage exceeds the total limit of containers 1Gi. Container app exceeded its local ephemeral storage limit "1Gi". `,
want: PodEviction{Kind: EvictionLocalStorageLimit, Resource: "ephemeral-storage",
Containers: []EvictedContainer{{Name: "app", Limit: "1Gi"}},
LimitDetail: `Container app exceeded its local ephemeral storage limit "1Gi".`},
},
{
name: "emptyDir limit",
message: `Usage of EmptyDir volume "cache" exceeds the limit "500Mi". `,
want: PodEviction{Kind: EvictionLocalStorageLimit, Resource: "ephemeral-storage",
LimitDetail: `Usage of EmptyDir volume "cache" exceeds the limit "500Mi".`},
},
{
name: "unparsed",
message: "Pod was evicted for an unknown reason",
want: PodEviction{Kind: EvictionUnparsed},
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
got := ParsePodEviction(tc.message)
if !reflect.DeepEqual(got, tc.want) {
t.Errorf("ParsePodEviction() = %+v, want %+v", got, tc.want)
}
if p := got.PressureCondition(); p != tc.pressure {
t.Errorf("PressureCondition() = %q, want %q", p, tc.pressure)
}
})
}
}
// The kubelet kills an evicted pod's containers, which then report Error or
// ContainerStatusUnknown with exit 137. That is a consequence of the eviction,
// so the pod-level reason and message must describe the pod.
func TestPodProblemReasonEvictedOutranksKilledContainers(t *testing.T) {
now := time.Date(2026, 11, 1, 12, 0, 0, 0, time.UTC)
const msg = "The node was low on resource: memory. Threshold quantity: 100Mi, available: 91844Ki. "
for _, term := range []corev1.ContainerStateTerminated{
{Reason: "Error", ExitCode: 137},
{Reason: "ContainerStatusUnknown", ExitCode: 137, Message: "The container could not be located when the pod was terminated"},
} {
pod := &corev1.Pod{Status: corev1.PodStatus{
Phase: corev1.PodFailed, Reason: "Evicted", Message: msg,
ContainerStatuses: []corev1.ContainerStatus{{Name: "app", State: corev1.ContainerState{Terminated: &term}}},
}}
if got := PodProblemReason(pod, now); got != "Evicted" {
t.Errorf("%s: PodProblemReason = %q, want Evicted", term.Reason, got)
}
if got := PodProblemMessage(pod); got != msg {
t.Errorf("%s: PodProblemMessage = %q, want the eviction message", term.Reason, got)
}
}
}
// The kubelet kills a sidecar on the way out (Error, exit 137); a container the
// kernel OOM-killed later in the list still names the pod's problem.
func TestPodProblemReasonEvictedKeepsOOMKilledInAnyPosition(t *testing.T) {
now := time.Date(2026, 11, 1, 12, 0, 0, 0, time.UTC)
pod := &corev1.Pod{Status: corev1.PodStatus{
Phase: corev1.PodFailed, Reason: "Evicted", Message: "The node was low on resource: memory. ",
ContainerStatuses: []corev1.ContainerStatus{
{Name: "sidecar", State: corev1.ContainerState{Terminated: &corev1.ContainerStateTerminated{Reason: "Error", ExitCode: 137}}},
{Name: "app", State: corev1.ContainerState{Terminated: &corev1.ContainerStateTerminated{Reason: "OOMKilled", ExitCode: 137, Message: "oom"}}},
},
}}
if got := PodProblemReason(pod, now); got != "OOMKilled" {
t.Errorf("PodProblemReason = %q, want OOMKilled", got)
}
if got := PodProblemMessage(pod); got != "oom" {
t.Errorf("PodProblemMessage = %q, want the OOM container's message", got)
}
}