mirror of
https://github.com/kubernetes-sigs/descheduler.git
synced 2026-01-28 06:29:29 +01:00
Merge pull request #1790 from ingvagabund/podlifetime-unit-tests
refactor(TestPodLifeTime): split the unit tests into smaller semantically close groups
This commit is contained in:
@@ -35,167 +35,145 @@ import (
|
|||||||
"sigs.k8s.io/descheduler/test"
|
"sigs.k8s.io/descheduler/test"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const nodeName1 = "n1"
|
||||||
|
|
||||||
|
var (
|
||||||
|
olderPodCreationTime = metav1.NewTime(time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC))
|
||||||
|
newerPodCreationTime = metav1.NewTime(time.Now())
|
||||||
|
)
|
||||||
|
|
||||||
|
func buildTestNode1() *v1.Node {
|
||||||
|
return test.BuildTestNode(nodeName1, 2000, 3000, 10, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildTestPodForNode1(name string, creationTime metav1.Time, apply func(*v1.Pod)) *v1.Pod {
|
||||||
|
return test.BuildTestPod(name, 100, 0, nodeName1, func(pod *v1.Pod) {
|
||||||
|
pod.ObjectMeta.CreationTimestamp = creationTime
|
||||||
|
if apply != nil {
|
||||||
|
apply(pod)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildTestPodWithRSOwnerRefForNode1(name string, creationTime metav1.Time, apply func(*v1.Pod)) *v1.Pod {
|
||||||
|
return buildTestPodForNode1(name, creationTime, func(pod *v1.Pod) {
|
||||||
|
test.SetRSOwnerRef(pod)
|
||||||
|
if apply != nil {
|
||||||
|
apply(pod)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildTestPodWithRSOwnerRefWithPendingPhaseForNode1(name string, creationTime metav1.Time, apply func(*v1.Pod)) *v1.Pod {
|
||||||
|
return buildTestPodWithRSOwnerRefForNode1(name, creationTime, func(pod *v1.Pod) {
|
||||||
|
pod.Status.Phase = "Pending"
|
||||||
|
if apply != nil {
|
||||||
|
apply(pod)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type podLifeTimeTestCase struct {
|
||||||
|
description string
|
||||||
|
args *PodLifeTimeArgs
|
||||||
|
pods []*v1.Pod
|
||||||
|
nodes []*v1.Node
|
||||||
|
expectedEvictedPods []string // if specified, will assert specific pods were evicted
|
||||||
|
expectedEvictedPodCount uint
|
||||||
|
ignorePvcPods bool
|
||||||
|
maxPodsToEvictPerNode *uint
|
||||||
|
maxPodsToEvictPerNamespace *uint
|
||||||
|
maxPodsToEvictTotal *uint
|
||||||
|
}
|
||||||
|
|
||||||
|
func runPodLifeTimeTest(t *testing.T, tc podLifeTimeTestCase) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
var objs []runtime.Object
|
||||||
|
for _, node := range tc.nodes {
|
||||||
|
objs = append(objs, node)
|
||||||
|
}
|
||||||
|
for _, pod := range tc.pods {
|
||||||
|
objs = append(objs, pod)
|
||||||
|
}
|
||||||
|
fakeClient := fake.NewSimpleClientset(objs...)
|
||||||
|
|
||||||
|
handle, podEvictor, err := frameworktesting.InitFrameworkHandle(
|
||||||
|
ctx,
|
||||||
|
fakeClient,
|
||||||
|
evictions.NewOptions().
|
||||||
|
WithMaxPodsToEvictPerNode(tc.maxPodsToEvictPerNode).
|
||||||
|
WithMaxPodsToEvictPerNamespace(tc.maxPodsToEvictPerNamespace).
|
||||||
|
WithMaxPodsToEvictTotal(tc.maxPodsToEvictTotal),
|
||||||
|
defaultevictor.DefaultEvictorArgs{IgnorePvcPods: tc.ignorePvcPods},
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unable to initialize a framework handle: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var evictedPods []string
|
||||||
|
test.RegisterEvictedPodsCollector(fakeClient, &evictedPods)
|
||||||
|
|
||||||
|
plugin, err := New(ctx, tc.args, handle)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unable to initialize the plugin: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
plugin.(frameworktypes.DeschedulePlugin).Deschedule(ctx, tc.nodes)
|
||||||
|
podsEvicted := podEvictor.TotalEvicted()
|
||||||
|
if podsEvicted != tc.expectedEvictedPodCount {
|
||||||
|
t.Errorf("Test error for description: %s. Expected evicted pods count %v, got %v", tc.description, tc.expectedEvictedPodCount, podsEvicted)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tc.expectedEvictedPods != nil {
|
||||||
|
diff := sets.New(tc.expectedEvictedPods...).Difference(sets.New(evictedPods...))
|
||||||
|
if diff.Len() > 0 {
|
||||||
|
t.Errorf(
|
||||||
|
"Expected pods %v to be evicted but %v were not evicted. Actual pods evicted: %v",
|
||||||
|
tc.expectedEvictedPods,
|
||||||
|
diff.UnsortedList(),
|
||||||
|
evictedPods,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestPodLifeTime(t *testing.T) {
|
func TestPodLifeTime(t *testing.T) {
|
||||||
const nodeName1 = "n1"
|
|
||||||
buildTestNode1 := func() *v1.Node {
|
|
||||||
return test.BuildTestNode(nodeName1, 2000, 3000, 10, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
olderPodCreationTime := metav1.NewTime(time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC))
|
|
||||||
newerPodCreationTime := metav1.NewTime(time.Now())
|
|
||||||
|
|
||||||
buildTestPodForNode1 := func(name string, creationTime metav1.Time, apply func(*v1.Pod)) *v1.Pod {
|
|
||||||
return test.BuildTestPod(name, 100, 0, nodeName1, func(pod *v1.Pod) {
|
|
||||||
pod.ObjectMeta.CreationTimestamp = creationTime
|
|
||||||
if apply != nil {
|
|
||||||
apply(pod)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
buildTestPodWithRSOwnerRefForNode1 := func(name string, creationTime metav1.Time, apply func(*v1.Pod)) *v1.Pod {
|
|
||||||
return buildTestPodForNode1(name, creationTime, func(pod *v1.Pod) {
|
|
||||||
test.SetRSOwnerRef(pod)
|
|
||||||
if apply != nil {
|
|
||||||
apply(pod)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
buildTestPodWithRSOwnerRefWithPendingPhaseForNode1 := func(name string, creationTime metav1.Time, apply func(*v1.Pod)) *v1.Pod {
|
|
||||||
return buildTestPodWithRSOwnerRefForNode1(name, creationTime, func(pod *v1.Pod) {
|
|
||||||
pod.Status.Phase = "Pending"
|
|
||||||
if apply != nil {
|
|
||||||
apply(pod)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
var maxLifeTime uint = 600
|
var maxLifeTime uint = 600
|
||||||
testCases := []struct {
|
testCases := []podLifeTimeTestCase{
|
||||||
description string
|
|
||||||
args *PodLifeTimeArgs
|
|
||||||
pods []*v1.Pod
|
|
||||||
nodes []*v1.Node
|
|
||||||
expectedEvictedPods []string // if specified, will assert specific pods were evicted
|
|
||||||
expectedEvictedPodCount uint
|
|
||||||
ignorePvcPods bool
|
|
||||||
maxPodsToEvictPerNode *uint
|
|
||||||
maxPodsToEvictPerNamespace *uint
|
|
||||||
maxPodsToEvictTotal *uint
|
|
||||||
}{
|
|
||||||
{
|
{
|
||||||
description: "Two pods in the default namespace, 1 is new and 1 very is old. 1 should be evicted.",
|
description: "No pod should be evicted since pod terminating",
|
||||||
args: &PodLifeTimeArgs{
|
args: &PodLifeTimeArgs{
|
||||||
MaxPodLifeTimeSeconds: &maxLifeTime,
|
MaxPodLifeTimeSeconds: &maxLifeTime,
|
||||||
|
LabelSelector: &metav1.LabelSelector{
|
||||||
|
MatchLabels: map[string]string{"foo": "bar"},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
pods: []*v1.Pod{
|
pods: []*v1.Pod{
|
||||||
buildTestPodWithRSOwnerRefForNode1("p1", newerPodCreationTime, nil),
|
buildTestPodWithRSOwnerRefForNode1("p14", olderPodCreationTime, func(pod *v1.Pod) {
|
||||||
buildTestPodWithRSOwnerRefForNode1("p2", olderPodCreationTime, nil),
|
pod.DeletionTimestamp = &metav1.Time{}
|
||||||
},
|
}),
|
||||||
nodes: []*v1.Node{buildTestNode1()},
|
buildTestPodWithRSOwnerRefForNode1("p15", olderPodCreationTime, func(pod *v1.Pod) {
|
||||||
expectedEvictedPodCount: 1,
|
pod.DeletionTimestamp = &metav1.Time{}
|
||||||
expectedEvictedPods: []string{"p2"},
|
}),
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "Two pods in the default namespace, 2 are new and 0 are old. 0 should be evicted.",
|
|
||||||
args: &PodLifeTimeArgs{
|
|
||||||
MaxPodLifeTimeSeconds: &maxLifeTime,
|
|
||||||
},
|
|
||||||
pods: []*v1.Pod{
|
|
||||||
buildTestPodWithRSOwnerRefForNode1("p3", newerPodCreationTime, nil),
|
|
||||||
buildTestPodWithRSOwnerRefForNode1("p4", newerPodCreationTime, nil),
|
|
||||||
},
|
},
|
||||||
nodes: []*v1.Node{buildTestNode1()},
|
nodes: []*v1.Node{buildTestNode1()},
|
||||||
expectedEvictedPodCount: 0,
|
expectedEvictedPodCount: 0,
|
||||||
},
|
},
|
||||||
{
|
}
|
||||||
description: "Two pods in the default namespace, 1 created 605 seconds ago. 1 should be evicted.",
|
|
||||||
args: &PodLifeTimeArgs{
|
for _, tc := range testCases {
|
||||||
MaxPodLifeTimeSeconds: &maxLifeTime,
|
t.Run(tc.description, func(t *testing.T) {
|
||||||
},
|
runPodLifeTimeTest(t, tc)
|
||||||
pods: []*v1.Pod{
|
})
|
||||||
buildTestPodWithRSOwnerRefForNode1("p5", newerPodCreationTime, nil),
|
}
|
||||||
buildTestPodWithRSOwnerRefForNode1("p6", metav1.NewTime(time.Now().Add(-time.Second*605)), nil),
|
}
|
||||||
},
|
|
||||||
nodes: []*v1.Node{buildTestNode1()},
|
func TestPodLifeTime_EvictorConfiguration(t *testing.T) {
|
||||||
expectedEvictedPodCount: 1,
|
var maxLifeTime uint = 600
|
||||||
expectedEvictedPods: []string{"p6"},
|
testCases := []podLifeTimeTestCase{
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "Two pods in the default namespace, 1 created 595 seconds ago. 0 should be evicted.",
|
|
||||||
args: &PodLifeTimeArgs{
|
|
||||||
MaxPodLifeTimeSeconds: &maxLifeTime,
|
|
||||||
},
|
|
||||||
pods: []*v1.Pod{
|
|
||||||
buildTestPodForNode1("p7", newerPodCreationTime, nil),
|
|
||||||
buildTestPodForNode1("p8", metav1.NewTime(time.Now().Add(-time.Second*595)), nil),
|
|
||||||
},
|
|
||||||
nodes: []*v1.Node{buildTestNode1()},
|
|
||||||
expectedEvictedPodCount: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "Two pods, one with ContainerCreating state. 1 should be evicted.",
|
|
||||||
args: &PodLifeTimeArgs{
|
|
||||||
MaxPodLifeTimeSeconds: &maxLifeTime,
|
|
||||||
States: []string{"ContainerCreating"},
|
|
||||||
},
|
|
||||||
pods: []*v1.Pod{
|
|
||||||
buildTestPodWithRSOwnerRefWithPendingPhaseForNode1("p9", olderPodCreationTime, nil),
|
|
||||||
test.BuildTestPod("container-creating-stuck", 0, 0, nodeName1, func(pod *v1.Pod) {
|
|
||||||
pod.Status.ContainerStatuses = []v1.ContainerStatus{
|
|
||||||
{
|
|
||||||
State: v1.ContainerState{
|
|
||||||
Waiting: &v1.ContainerStateWaiting{Reason: "ContainerCreating"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
test.SetRSOwnerRef(pod)
|
|
||||||
pod.ObjectMeta.CreationTimestamp = olderPodCreationTime
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
nodes: []*v1.Node{buildTestNode1()},
|
|
||||||
expectedEvictedPodCount: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "Two pods, one with PodInitializing state. 1 should be evicted.",
|
|
||||||
args: &PodLifeTimeArgs{
|
|
||||||
MaxPodLifeTimeSeconds: &maxLifeTime,
|
|
||||||
States: []string{"PodInitializing"},
|
|
||||||
},
|
|
||||||
pods: []*v1.Pod{
|
|
||||||
buildTestPodWithRSOwnerRefWithPendingPhaseForNode1("p9", olderPodCreationTime, nil),
|
|
||||||
test.BuildTestPod("pod-initializing-stuck", 0, 0, nodeName1, func(pod *v1.Pod) {
|
|
||||||
pod.Status.ContainerStatuses = []v1.ContainerStatus{
|
|
||||||
{
|
|
||||||
State: v1.ContainerState{
|
|
||||||
Waiting: &v1.ContainerStateWaiting{Reason: "PodInitializing"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
test.SetRSOwnerRef(pod)
|
|
||||||
pod.ObjectMeta.CreationTimestamp = olderPodCreationTime
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
nodes: []*v1.Node{buildTestNode1()},
|
|
||||||
expectedEvictedPodCount: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "Two old pods with different states. 1 should be evicted.",
|
|
||||||
args: &PodLifeTimeArgs{
|
|
||||||
MaxPodLifeTimeSeconds: &maxLifeTime,
|
|
||||||
States: []string{"Pending"},
|
|
||||||
},
|
|
||||||
pods: []*v1.Pod{
|
|
||||||
buildTestPodWithRSOwnerRefWithPendingPhaseForNode1("p9", olderPodCreationTime, nil),
|
|
||||||
buildTestPodWithRSOwnerRefForNode1("p10", olderPodCreationTime, func(pod *v1.Pod) {
|
|
||||||
pod.Status.Phase = "Running"
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
nodes: []*v1.Node{buildTestNode1()},
|
|
||||||
expectedEvictedPodCount: 1,
|
|
||||||
expectedEvictedPods: []string{"p9"},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
description: "Does not evict pvc pods with ignorePvcPods set to true",
|
description: "Does not evict pvc pods with ignorePvcPods set to true",
|
||||||
args: &PodLifeTimeArgs{
|
args: &PodLifeTimeArgs{
|
||||||
@@ -235,6 +213,18 @@ func TestPodLifeTime(t *testing.T) {
|
|||||||
nodes: []*v1.Node{buildTestNode1()},
|
nodes: []*v1.Node{buildTestNode1()},
|
||||||
expectedEvictedPodCount: 1,
|
expectedEvictedPodCount: 1,
|
||||||
},
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.description, func(t *testing.T) {
|
||||||
|
runPodLifeTimeTest(t, tc)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPodLifeTime_GenericFiltering(t *testing.T) {
|
||||||
|
var maxLifeTime uint = 600
|
||||||
|
testCases := []podLifeTimeTestCase{
|
||||||
{
|
{
|
||||||
description: "1 pod matching label selector should be evicted",
|
description: "1 pod matching label selector should be evicted",
|
||||||
args: &PodLifeTimeArgs{
|
args: &PodLifeTimeArgs{
|
||||||
@@ -255,25 +245,18 @@ func TestPodLifeTime(t *testing.T) {
|
|||||||
expectedEvictedPodCount: 1,
|
expectedEvictedPodCount: 1,
|
||||||
expectedEvictedPods: []string{"p12"},
|
expectedEvictedPods: []string{"p12"},
|
||||||
},
|
},
|
||||||
{
|
}
|
||||||
description: "No pod should be evicted since pod terminating",
|
|
||||||
args: &PodLifeTimeArgs{
|
for _, tc := range testCases {
|
||||||
MaxPodLifeTimeSeconds: &maxLifeTime,
|
t.Run(tc.description, func(t *testing.T) {
|
||||||
LabelSelector: &metav1.LabelSelector{
|
runPodLifeTimeTest(t, tc)
|
||||||
MatchLabels: map[string]string{"foo": "bar"},
|
})
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
pods: []*v1.Pod{
|
|
||||||
buildTestPodWithRSOwnerRefForNode1("p14", olderPodCreationTime, func(pod *v1.Pod) {
|
func TestPodLifeTime_EvictionLimits(t *testing.T) {
|
||||||
pod.DeletionTimestamp = &metav1.Time{}
|
var maxLifeTime uint = 600
|
||||||
}),
|
testCases := []podLifeTimeTestCase{
|
||||||
buildTestPodWithRSOwnerRefForNode1("p15", olderPodCreationTime, func(pod *v1.Pod) {
|
|
||||||
pod.DeletionTimestamp = &metav1.Time{}
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
nodes: []*v1.Node{buildTestNode1()},
|
|
||||||
expectedEvictedPodCount: 0,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
description: "2 Oldest pods should be evicted when maxPodsToEvictPerNode and maxPodsToEvictPerNamespace are not set",
|
description: "2 Oldest pods should be evicted when maxPodsToEvictPerNode and maxPodsToEvictPerNamespace are not set",
|
||||||
args: &PodLifeTimeArgs{
|
args: &PodLifeTimeArgs{
|
||||||
@@ -333,6 +316,64 @@ func TestPodLifeTime(t *testing.T) {
|
|||||||
expectedEvictedPodCount: 1,
|
expectedEvictedPodCount: 1,
|
||||||
expectedEvictedPods: []string{"p2"},
|
expectedEvictedPods: []string{"p2"},
|
||||||
},
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.description, func(t *testing.T) {
|
||||||
|
runPodLifeTimeTest(t, tc)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPodLifeTime_ContainerWaitingReasons(t *testing.T) {
|
||||||
|
var maxLifeTime uint = 600
|
||||||
|
testCases := []podLifeTimeTestCase{
|
||||||
|
{
|
||||||
|
description: "Two pods, one with ContainerCreating state. 1 should be evicted.",
|
||||||
|
args: &PodLifeTimeArgs{
|
||||||
|
MaxPodLifeTimeSeconds: &maxLifeTime,
|
||||||
|
States: []string{"ContainerCreating"},
|
||||||
|
},
|
||||||
|
pods: []*v1.Pod{
|
||||||
|
buildTestPodWithRSOwnerRefWithPendingPhaseForNode1("p9", olderPodCreationTime, nil),
|
||||||
|
test.BuildTestPod("container-creating-stuck", 0, 0, nodeName1, func(pod *v1.Pod) {
|
||||||
|
pod.Status.ContainerStatuses = []v1.ContainerStatus{
|
||||||
|
{
|
||||||
|
State: v1.ContainerState{
|
||||||
|
Waiting: &v1.ContainerStateWaiting{Reason: "ContainerCreating"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
test.SetRSOwnerRef(pod)
|
||||||
|
pod.ObjectMeta.CreationTimestamp = olderPodCreationTime
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
nodes: []*v1.Node{buildTestNode1()},
|
||||||
|
expectedEvictedPodCount: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Two pods, one with PodInitializing state. 1 should be evicted.",
|
||||||
|
args: &PodLifeTimeArgs{
|
||||||
|
MaxPodLifeTimeSeconds: &maxLifeTime,
|
||||||
|
States: []string{"PodInitializing"},
|
||||||
|
},
|
||||||
|
pods: []*v1.Pod{
|
||||||
|
buildTestPodWithRSOwnerRefWithPendingPhaseForNode1("p9", olderPodCreationTime, nil),
|
||||||
|
test.BuildTestPod("pod-initializing-stuck", 0, 0, nodeName1, func(pod *v1.Pod) {
|
||||||
|
pod.Status.ContainerStatuses = []v1.ContainerStatus{
|
||||||
|
{
|
||||||
|
State: v1.ContainerState{
|
||||||
|
Waiting: &v1.ContainerStateWaiting{Reason: "PodInitializing"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
test.SetRSOwnerRef(pod)
|
||||||
|
pod.ObjectMeta.CreationTimestamp = olderPodCreationTime
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
nodes: []*v1.Node{buildTestNode1()},
|
||||||
|
expectedEvictedPodCount: 1,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
description: "1 pod with container status ImagePullBackOff should be evicted",
|
description: "1 pod with container status ImagePullBackOff should be evicted",
|
||||||
args: &PodLifeTimeArgs{
|
args: &PodLifeTimeArgs{
|
||||||
@@ -535,6 +576,38 @@ func TestPodLifeTime(t *testing.T) {
|
|||||||
nodes: []*v1.Node{buildTestNode1()},
|
nodes: []*v1.Node{buildTestNode1()},
|
||||||
expectedEvictedPodCount: 1,
|
expectedEvictedPodCount: 1,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
description: "1 pod with ImagePullBackOff status should be ignored when States filter is ContainerCreating",
|
||||||
|
args: &PodLifeTimeArgs{
|
||||||
|
MaxPodLifeTimeSeconds: &maxLifeTime,
|
||||||
|
States: []string{"ContainerCreating"},
|
||||||
|
},
|
||||||
|
pods: []*v1.Pod{
|
||||||
|
buildTestPodWithRSOwnerRefWithPendingPhaseForNode1("p9", olderPodCreationTime, func(pod *v1.Pod) {
|
||||||
|
pod.Status.ContainerStatuses = []v1.ContainerStatus{
|
||||||
|
{
|
||||||
|
State: v1.ContainerState{
|
||||||
|
Waiting: &v1.ContainerStateWaiting{Reason: "ImagePullBackOff"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
nodes: []*v1.Node{buildTestNode1()},
|
||||||
|
expectedEvictedPodCount: 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.description, func(t *testing.T) {
|
||||||
|
runPodLifeTimeTest(t, tc)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPodLifeTime_PodStatusReasons(t *testing.T) {
|
||||||
|
var maxLifeTime uint = 600
|
||||||
|
testCases := []podLifeTimeTestCase{
|
||||||
{
|
{
|
||||||
description: "1 pod with pod status reason NodeLost should be evicted",
|
description: "1 pod with pod status reason NodeLost should be evicted",
|
||||||
args: &PodLifeTimeArgs{
|
args: &PodLifeTimeArgs{
|
||||||
@@ -591,6 +664,96 @@ func TestPodLifeTime(t *testing.T) {
|
|||||||
nodes: []*v1.Node{buildTestNode1()},
|
nodes: []*v1.Node{buildTestNode1()},
|
||||||
expectedEvictedPodCount: 1,
|
expectedEvictedPodCount: 1,
|
||||||
},
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.description, func(t *testing.T) {
|
||||||
|
runPodLifeTimeTest(t, tc)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPodLifeTime_AgeThreshold(t *testing.T) {
|
||||||
|
var maxLifeTime uint = 600
|
||||||
|
testCases := []podLifeTimeTestCase{
|
||||||
|
{
|
||||||
|
description: "Two pods in the default namespace, 1 is new and 1 very is old. 1 should be evicted.",
|
||||||
|
args: &PodLifeTimeArgs{
|
||||||
|
MaxPodLifeTimeSeconds: &maxLifeTime,
|
||||||
|
},
|
||||||
|
pods: []*v1.Pod{
|
||||||
|
buildTestPodWithRSOwnerRefForNode1("p1", newerPodCreationTime, nil),
|
||||||
|
buildTestPodWithRSOwnerRefForNode1("p2", olderPodCreationTime, nil),
|
||||||
|
},
|
||||||
|
nodes: []*v1.Node{buildTestNode1()},
|
||||||
|
expectedEvictedPodCount: 1,
|
||||||
|
expectedEvictedPods: []string{"p2"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Two pods in the default namespace, 2 are new and 0 are old. 0 should be evicted.",
|
||||||
|
args: &PodLifeTimeArgs{
|
||||||
|
MaxPodLifeTimeSeconds: &maxLifeTime,
|
||||||
|
},
|
||||||
|
pods: []*v1.Pod{
|
||||||
|
buildTestPodWithRSOwnerRefForNode1("p3", newerPodCreationTime, nil),
|
||||||
|
buildTestPodWithRSOwnerRefForNode1("p4", newerPodCreationTime, nil),
|
||||||
|
},
|
||||||
|
nodes: []*v1.Node{buildTestNode1()},
|
||||||
|
expectedEvictedPodCount: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Two pods in the default namespace, 1 created 605 seconds ago. 1 should be evicted.",
|
||||||
|
args: &PodLifeTimeArgs{
|
||||||
|
MaxPodLifeTimeSeconds: &maxLifeTime,
|
||||||
|
},
|
||||||
|
pods: []*v1.Pod{
|
||||||
|
buildTestPodWithRSOwnerRefForNode1("p5", newerPodCreationTime, nil),
|
||||||
|
buildTestPodWithRSOwnerRefForNode1("p6", metav1.NewTime(time.Now().Add(-time.Second*605)), nil),
|
||||||
|
},
|
||||||
|
nodes: []*v1.Node{buildTestNode1()},
|
||||||
|
expectedEvictedPodCount: 1,
|
||||||
|
expectedEvictedPods: []string{"p6"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Two pods in the default namespace, 1 created 595 seconds ago. 0 should be evicted.",
|
||||||
|
args: &PodLifeTimeArgs{
|
||||||
|
MaxPodLifeTimeSeconds: &maxLifeTime,
|
||||||
|
},
|
||||||
|
pods: []*v1.Pod{
|
||||||
|
buildTestPodForNode1("p7", newerPodCreationTime, nil),
|
||||||
|
buildTestPodForNode1("p8", metav1.NewTime(time.Now().Add(-time.Second*595)), nil),
|
||||||
|
},
|
||||||
|
nodes: []*v1.Node{buildTestNode1()},
|
||||||
|
expectedEvictedPodCount: 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.description, func(t *testing.T) {
|
||||||
|
runPodLifeTimeTest(t, tc)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPodLifeTime_PodPhaseStates(t *testing.T) {
|
||||||
|
var maxLifeTime uint = 600
|
||||||
|
testCases := []podLifeTimeTestCase{
|
||||||
|
{
|
||||||
|
description: "Two old pods with different states. 1 should be evicted.",
|
||||||
|
args: &PodLifeTimeArgs{
|
||||||
|
MaxPodLifeTimeSeconds: &maxLifeTime,
|
||||||
|
States: []string{"Pending"},
|
||||||
|
},
|
||||||
|
pods: []*v1.Pod{
|
||||||
|
buildTestPodWithRSOwnerRefWithPendingPhaseForNode1("p9", olderPodCreationTime, nil),
|
||||||
|
buildTestPodWithRSOwnerRefForNode1("p10", olderPodCreationTime, func(pod *v1.Pod) {
|
||||||
|
pod.Status.Phase = "Running"
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
nodes: []*v1.Node{buildTestNode1()},
|
||||||
|
expectedEvictedPodCount: 1,
|
||||||
|
expectedEvictedPods: []string{"p9"},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
description: "1 pod with pod status phase v1.PodSucceeded should be evicted",
|
description: "1 pod with pod status phase v1.PodSucceeded should be evicted",
|
||||||
args: &PodLifeTimeArgs{
|
args: &PodLifeTimeArgs{
|
||||||
@@ -633,81 +796,11 @@ func TestPodLifeTime(t *testing.T) {
|
|||||||
nodes: []*v1.Node{buildTestNode1()},
|
nodes: []*v1.Node{buildTestNode1()},
|
||||||
expectedEvictedPodCount: 1,
|
expectedEvictedPodCount: 1,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
description: "1 pod with ImagePullBackOff status should be ignored when States filter is ContainerCreating",
|
|
||||||
args: &PodLifeTimeArgs{
|
|
||||||
MaxPodLifeTimeSeconds: &maxLifeTime,
|
|
||||||
States: []string{"ContainerCreating"},
|
|
||||||
},
|
|
||||||
pods: []*v1.Pod{
|
|
||||||
buildTestPodWithRSOwnerRefWithPendingPhaseForNode1("p9", olderPodCreationTime, func(pod *v1.Pod) {
|
|
||||||
pod.Status.ContainerStatuses = []v1.ContainerStatus{
|
|
||||||
{
|
|
||||||
State: v1.ContainerState{
|
|
||||||
Waiting: &v1.ContainerStateWaiting{Reason: "ImagePullBackOff"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
nodes: []*v1.Node{buildTestNode1()},
|
|
||||||
expectedEvictedPodCount: 0,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.description, func(t *testing.T) {
|
t.Run(tc.description, func(t *testing.T) {
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
runPodLifeTimeTest(t, tc)
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
var objs []runtime.Object
|
|
||||||
for _, node := range tc.nodes {
|
|
||||||
objs = append(objs, node)
|
|
||||||
}
|
|
||||||
for _, pod := range tc.pods {
|
|
||||||
objs = append(objs, pod)
|
|
||||||
}
|
|
||||||
fakeClient := fake.NewSimpleClientset(objs...)
|
|
||||||
|
|
||||||
handle, podEvictor, err := frameworktesting.InitFrameworkHandle(
|
|
||||||
ctx,
|
|
||||||
fakeClient,
|
|
||||||
evictions.NewOptions().
|
|
||||||
WithMaxPodsToEvictPerNode(tc.maxPodsToEvictPerNode).
|
|
||||||
WithMaxPodsToEvictPerNamespace(tc.maxPodsToEvictPerNamespace).
|
|
||||||
WithMaxPodsToEvictTotal(tc.maxPodsToEvictTotal),
|
|
||||||
defaultevictor.DefaultEvictorArgs{IgnorePvcPods: tc.ignorePvcPods},
|
|
||||||
nil,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unable to initialize a framework handle: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var evictedPods []string
|
|
||||||
test.RegisterEvictedPodsCollector(fakeClient, &evictedPods)
|
|
||||||
|
|
||||||
plugin, err := New(ctx, tc.args, handle)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unable to initialize the plugin: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
plugin.(frameworktypes.DeschedulePlugin).Deschedule(ctx, tc.nodes)
|
|
||||||
podsEvicted := podEvictor.TotalEvicted()
|
|
||||||
if podsEvicted != tc.expectedEvictedPodCount {
|
|
||||||
t.Errorf("Test error for description: %s. Expected evicted pods count %v, got %v", tc.description, tc.expectedEvictedPodCount, podsEvicted)
|
|
||||||
}
|
|
||||||
|
|
||||||
if tc.expectedEvictedPods != nil {
|
|
||||||
diff := sets.New(tc.expectedEvictedPods...).Difference(sets.New(evictedPods...))
|
|
||||||
if diff.Len() > 0 {
|
|
||||||
t.Errorf(
|
|
||||||
"Expected pods %v to be evicted but %v were not evicted. Actual pods evicted: %v",
|
|
||||||
tc.expectedEvictedPods,
|
|
||||||
diff.UnsortedList(),
|
|
||||||
evictedPods,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user