diff --git a/pkg/descheduler/strategies/pod_lifetime.go b/pkg/descheduler/strategies/pod_lifetime.go index 93cd28b93..5fa33d21a 100644 --- a/pkg/descheduler/strategies/pod_lifetime.go +++ b/pkg/descheduler/strategies/pod_lifetime.go @@ -31,16 +31,25 @@ import ( podutil "sigs.k8s.io/descheduler/pkg/descheduler/pod" ) +var ( + podLifetimeAllowedPhases = sets.NewString( + string(v1.PodRunning), + string(v1.PodPending), + + // Container state reason list: https://github.com/kubernetes/kubernetes/blob/release-1.24/pkg/kubelet/kubelet_pods.go#L76-L79 + "PodInitializing", + "ContainerCreating", + ) +) + func validatePodLifeTimeParams(params *api.StrategyParameters) error { if params == nil || params.PodLifeTime == nil || params.PodLifeTime.MaxPodLifeTimeSeconds == nil { return fmt.Errorf("MaxPodLifeTimeSeconds not set") } if params.PodLifeTime.PodStatusPhases != nil { - for _, phase := range params.PodLifeTime.PodStatusPhases { - if phase != string(v1.PodPending) && phase != string(v1.PodRunning) { - return fmt.Errorf("only Pending and Running phases are supported in PodLifeTime") - } + if !podLifetimeAllowedPhases.HasAll(params.PodLifeTime.PodStatusPhases...) { + return fmt.Errorf("PodStatusPhases must be one of %v", podLifetimeAllowedPhases.List()) } } @@ -75,7 +84,14 @@ func PodLifeTime(ctx context.Context, client clientset.Interface, strategy api.D if string(pod.Status.Phase) == phase { return evictorFilter.Filter(pod) } + + for _, containerStatus := range pod.Status.ContainerStatuses { + if containerStatus.State.Waiting != nil && containerStatus.State.Waiting.Reason == phase { + return evictable.IsEvictable(pod) + } + } } + return false } } diff --git a/pkg/descheduler/strategies/pod_lifetime_test.go b/pkg/descheduler/strategies/pod_lifetime_test.go index b3998b919..e8a01ea79 100644 --- a/pkg/descheduler/strategies/pod_lifetime_test.go +++ b/pkg/descheduler/strategies/pod_lifetime_test.go @@ -197,7 +197,61 @@ func TestPodLifeTime(t *testing.T) { expectedEvictedPodCount: 0, }, { - description: "Two old pods with different status phases. 1 should be evicted.", + description: "Two pods, one with ContainerCreating state. 1 should be evicted.", + strategy: api.DeschedulerStrategy{ + Enabled: true, + Params: &api.StrategyParameters{ + PodLifeTime: &api.PodLifeTime{ + MaxPodLifeTimeSeconds: &maxLifeTime, + PodStatusPhases: []string{"ContainerCreating"}, + }, + }, + }, + pods: []*v1.Pod{ + p9, + test.BuildTestPod("container-creating-stuck", 0, 0, node1.Name, func(pod *v1.Pod) { + pod.Status.ContainerStatuses = []v1.ContainerStatus{ + { + State: v1.ContainerState{ + Waiting: &v1.ContainerStateWaiting{Reason: "ContainerCreating"}, + }, + }, + } + pod.OwnerReferences = ownerRef1 + }), + }, + nodes: []*v1.Node{node1}, + expectedEvictedPodCount: 1, + }, + { + description: "Two pods, one with PodInitializing state. 1 should be evicted.", + strategy: api.DeschedulerStrategy{ + Enabled: true, + Params: &api.StrategyParameters{ + PodLifeTime: &api.PodLifeTime{ + MaxPodLifeTimeSeconds: &maxLifeTime, + PodStatusPhases: []string{"PodInitializing"}, + }, + }, + }, + pods: []*v1.Pod{ + p9, + test.BuildTestPod("pod-initializing-stuck", 0, 0, node1.Name, func(pod *v1.Pod) { + pod.Status.ContainerStatuses = []v1.ContainerStatus{ + { + State: v1.ContainerState{ + Waiting: &v1.ContainerStateWaiting{Reason: "PodInitializing"}, + }, + }, + } + pod.OwnerReferences = ownerRef1 + }), + }, + nodes: []*v1.Node{node1}, + expectedEvictedPodCount: 1, + }, + { + description: "Two old pods with different states. 1 should be evicted.", strategy: api.DeschedulerStrategy{ Enabled: true, Params: &api.StrategyParameters{