mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-08 19:47:56 +00:00
Merge pull request #125461 from mimowo/pod-disruption-conditions-ga
Graduate PodDisruptionConditions to stable
This commit is contained in:
commit
4a214f6ad9
@ -2709,7 +2709,6 @@ func TestSyncJobWithJobPodFailurePolicy(t *testing.T) {
|
|||||||
|
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
enableJobPodFailurePolicy bool
|
enableJobPodFailurePolicy bool
|
||||||
enablePodDisruptionConditions bool
|
|
||||||
enableJobPodReplacementPolicy bool
|
enableJobPodReplacementPolicy bool
|
||||||
job batch.Job
|
job batch.Job
|
||||||
pods []v1.Pod
|
pods []v1.Pod
|
||||||
@ -3702,7 +3701,7 @@ func TestSyncJobWithJobPodFailurePolicy(t *testing.T) {
|
|||||||
wantStatusFailed: 1,
|
wantStatusFailed: 1,
|
||||||
wantStatusSucceeded: 0,
|
wantStatusSucceeded: 0,
|
||||||
},
|
},
|
||||||
"terminating Pod considered failed when PodDisruptionConditions is disabled": {
|
"terminating Pod not considered failed when JobPodFailurePolicy is enabled and used": {
|
||||||
enableJobPodFailurePolicy: true,
|
enableJobPodFailurePolicy: true,
|
||||||
job: batch.Job{
|
job: batch.Job{
|
||||||
TypeMeta: metav1.TypeMeta{Kind: "Job"},
|
TypeMeta: metav1.TypeMeta{Kind: "Job"},
|
||||||
@ -3727,40 +3726,6 @@ func TestSyncJobWithJobPodFailurePolicy(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
pods: []v1.Pod{
|
|
||||||
{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
DeletionTimestamp: &now,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"terminating Pod not considered failed when PodDisruptionConditions is enabled": {
|
|
||||||
enableJobPodFailurePolicy: true,
|
|
||||||
enablePodDisruptionConditions: true,
|
|
||||||
job: batch.Job{
|
|
||||||
TypeMeta: metav1.TypeMeta{Kind: "Job"},
|
|
||||||
ObjectMeta: validObjectMeta,
|
|
||||||
Spec: batch.JobSpec{
|
|
||||||
Parallelism: ptr.To[int32](1),
|
|
||||||
Selector: validSelector,
|
|
||||||
Template: validTemplate,
|
|
||||||
BackoffLimit: ptr.To[int32](0),
|
|
||||||
PodFailurePolicy: &batch.PodFailurePolicy{
|
|
||||||
Rules: []batch.PodFailurePolicyRule{
|
|
||||||
{
|
|
||||||
Action: batch.PodFailurePolicyActionCount,
|
|
||||||
OnPodConditions: []batch.PodFailurePolicyOnPodConditionsPattern{
|
|
||||||
{
|
|
||||||
Type: v1.DisruptionTarget,
|
|
||||||
Status: v1.ConditionTrue,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
pods: []v1.Pod{
|
pods: []v1.Pod{
|
||||||
{
|
{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
@ -3776,7 +3741,6 @@ func TestSyncJobWithJobPodFailurePolicy(t *testing.T) {
|
|||||||
for name, tc := range testCases {
|
for name, tc := range testCases {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.JobPodFailurePolicy, tc.enableJobPodFailurePolicy)
|
featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.JobPodFailurePolicy, tc.enableJobPodFailurePolicy)
|
||||||
featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.PodDisruptionConditions, tc.enablePodDisruptionConditions)
|
|
||||||
featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.JobPodReplacementPolicy, tc.enableJobPodReplacementPolicy)
|
featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.JobPodReplacementPolicy, tc.enableJobPodReplacementPolicy)
|
||||||
|
|
||||||
if tc.job.Spec.PodReplacementPolicy == nil {
|
if tc.job.Spec.PodReplacementPolicy == nil {
|
||||||
|
@ -31,20 +31,17 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
"k8s.io/apimachinery/pkg/util/strategicpatch"
|
"k8s.io/apimachinery/pkg/util/strategicpatch"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
"k8s.io/client-go/informers"
|
"k8s.io/client-go/informers"
|
||||||
coreinformers "k8s.io/client-go/informers/core/v1"
|
coreinformers "k8s.io/client-go/informers/core/v1"
|
||||||
clientset "k8s.io/client-go/kubernetes"
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/client-go/kubernetes/fake"
|
"k8s.io/client-go/kubernetes/fake"
|
||||||
clienttesting "k8s.io/client-go/testing"
|
clienttesting "k8s.io/client-go/testing"
|
||||||
"k8s.io/client-go/util/workqueue"
|
"k8s.io/client-go/util/workqueue"
|
||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
|
||||||
metricstestutil "k8s.io/component-base/metrics/testutil"
|
metricstestutil "k8s.io/component-base/metrics/testutil"
|
||||||
"k8s.io/klog/v2/ktesting"
|
"k8s.io/klog/v2/ktesting"
|
||||||
"k8s.io/kubernetes/pkg/controller"
|
"k8s.io/kubernetes/pkg/controller"
|
||||||
"k8s.io/kubernetes/pkg/controller/podgc/metrics"
|
"k8s.io/kubernetes/pkg/controller/podgc/metrics"
|
||||||
"k8s.io/kubernetes/pkg/controller/testutil"
|
"k8s.io/kubernetes/pkg/controller/testutil"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
"k8s.io/kubernetes/pkg/kubelet/eviction"
|
"k8s.io/kubernetes/pkg/kubelet/eviction"
|
||||||
testingclock "k8s.io/utils/clock/testing"
|
testingclock "k8s.io/utils/clock/testing"
|
||||||
"k8s.io/utils/pointer"
|
"k8s.io/utils/pointer"
|
||||||
@ -69,23 +66,21 @@ func TestGCTerminated(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
pods []nameToPhase
|
pods []nameToPhase
|
||||||
threshold int
|
threshold int
|
||||||
deletedPodNames sets.Set[string]
|
deletedPodNames sets.Set[string]
|
||||||
patchedPodNames sets.Set[string]
|
patchedPodNames sets.Set[string]
|
||||||
enablePodDisruptionConditions bool
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "delete pod a which is PodFailed and pod b which is PodSucceeded; PodDisruptionConditions enabled",
|
name: "delete pod a which is PodFailed and pod b which is PodSucceeded",
|
||||||
pods: []nameToPhase{
|
pods: []nameToPhase{
|
||||||
{name: "a", phase: v1.PodFailed},
|
{name: "a", phase: v1.PodFailed},
|
||||||
{name: "b", phase: v1.PodSucceeded},
|
{name: "b", phase: v1.PodSucceeded},
|
||||||
{name: "c", phase: v1.PodFailed},
|
{name: "c", phase: v1.PodFailed},
|
||||||
},
|
},
|
||||||
threshold: 1,
|
threshold: 1,
|
||||||
deletedPodNames: sets.New("a", "b"),
|
deletedPodNames: sets.New("a", "b"),
|
||||||
enablePodDisruptionConditions: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "threshold = 0, disables terminated pod deletion",
|
name: "threshold = 0, disables terminated pod deletion",
|
||||||
@ -156,7 +151,6 @@ func TestGCTerminated(t *testing.T) {
|
|||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
resetMetrics()
|
resetMetrics()
|
||||||
_, ctx := ktesting.NewTestContext(t)
|
_, ctx := ktesting.NewTestContext(t)
|
||||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodDisruptionConditions, test.enablePodDisruptionConditions)
|
|
||||||
creationTime := time.Unix(0, 0)
|
creationTime := time.Unix(0, 0)
|
||||||
nodes := []*v1.Node{testutil.NewNode("node")}
|
nodes := []*v1.Node{testutil.NewNode("node")}
|
||||||
|
|
||||||
@ -206,19 +200,18 @@ func waitForAdded(q workqueue.TypedDelayingInterface[string], depth int) error {
|
|||||||
|
|
||||||
func TestGCOrphaned(t *testing.T) {
|
func TestGCOrphaned(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
initialClientNodes []*v1.Node
|
initialClientNodes []*v1.Node
|
||||||
initialInformerNodes []*v1.Node
|
initialInformerNodes []*v1.Node
|
||||||
delay time.Duration
|
delay time.Duration
|
||||||
addedClientNodes []*v1.Node
|
addedClientNodes []*v1.Node
|
||||||
deletedClientNodes []*v1.Node
|
deletedClientNodes []*v1.Node
|
||||||
addedInformerNodes []*v1.Node
|
addedInformerNodes []*v1.Node
|
||||||
deletedInformerNodes []*v1.Node
|
deletedInformerNodes []*v1.Node
|
||||||
pods []*v1.Pod
|
pods []*v1.Pod
|
||||||
itemsInQueue int
|
itemsInQueue int
|
||||||
deletedPodNames sets.Set[string]
|
deletedPodNames sets.Set[string]
|
||||||
patchedPodNames sets.Set[string]
|
patchedPodNames sets.Set[string]
|
||||||
enablePodDisruptionConditions bool
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "nodes present in lister",
|
name: "nodes present in lister",
|
||||||
@ -259,17 +252,16 @@ func TestGCOrphaned(t *testing.T) {
|
|||||||
deletedPodNames: sets.New("a", "b"),
|
deletedPodNames: sets.New("a", "b"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no nodes with PodDisruptionConditions enabled",
|
name: "no nodes, one running pod",
|
||||||
delay: 2 * quarantineTime,
|
delay: 2 * quarantineTime,
|
||||||
pods: []*v1.Pod{
|
pods: []*v1.Pod{
|
||||||
makePod("a", "deleted", v1.PodFailed),
|
makePod("a", "deleted", v1.PodFailed),
|
||||||
makePod("b", "deleted", v1.PodSucceeded),
|
makePod("b", "deleted", v1.PodSucceeded),
|
||||||
makePod("c", "deleted", v1.PodRunning),
|
makePod("c", "deleted", v1.PodRunning),
|
||||||
},
|
},
|
||||||
itemsInQueue: 1,
|
itemsInQueue: 1,
|
||||||
deletedPodNames: sets.New("a", "b", "c"),
|
deletedPodNames: sets.New("a", "b", "c"),
|
||||||
patchedPodNames: sets.New("c"),
|
patchedPodNames: sets.New("c"),
|
||||||
enablePodDisruptionConditions: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "quarantine not finished",
|
name: "quarantine not finished",
|
||||||
@ -351,7 +343,6 @@ func TestGCOrphaned(t *testing.T) {
|
|||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
resetMetrics()
|
resetMetrics()
|
||||||
_, ctx := ktesting.NewTestContext(t)
|
_, ctx := ktesting.NewTestContext(t)
|
||||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodDisruptionConditions, test.enablePodDisruptionConditions)
|
|
||||||
|
|
||||||
client := setupNewSimpleClient(test.initialClientNodes, test.pods)
|
client := setupNewSimpleClient(test.initialClientNodes, test.pods)
|
||||||
gcc, podInformer, nodeInformer := NewFromClient(ctx, client, -1)
|
gcc, podInformer, nodeInformer := NewFromClient(ctx, client, -1)
|
||||||
@ -416,23 +407,11 @@ func TestGCUnscheduledTerminating(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
pods []nameToPhase
|
pods []nameToPhase
|
||||||
deletedPodNames sets.Set[string]
|
deletedPodNames sets.Set[string]
|
||||||
patchedPodNames sets.Set[string]
|
patchedPodNames sets.Set[string]
|
||||||
enablePodDisruptionConditions bool
|
|
||||||
}{
|
}{
|
||||||
{
|
|
||||||
name: "Unscheduled pod in any phase must be deleted, the phase of the running pod is changed to Failed; PodDisruptionConditions enabled",
|
|
||||||
pods: []nameToPhase{
|
|
||||||
{name: "a", phase: v1.PodFailed, deletionTimeStamp: &metav1.Time{}, nodeName: ""},
|
|
||||||
{name: "b", phase: v1.PodSucceeded, deletionTimeStamp: &metav1.Time{}, nodeName: ""},
|
|
||||||
{name: "c", phase: v1.PodRunning, deletionTimeStamp: &metav1.Time{}, nodeName: ""},
|
|
||||||
},
|
|
||||||
deletedPodNames: sets.New("a", "b", "c"),
|
|
||||||
patchedPodNames: sets.New("c"),
|
|
||||||
enablePodDisruptionConditions: true,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "Unscheduled pod in any phase must be deleted",
|
name: "Unscheduled pod in any phase must be deleted",
|
||||||
pods: []nameToPhase{
|
pods: []nameToPhase{
|
||||||
@ -457,7 +436,6 @@ func TestGCUnscheduledTerminating(t *testing.T) {
|
|||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
resetMetrics()
|
resetMetrics()
|
||||||
_, ctx := ktesting.NewTestContext(t)
|
_, ctx := ktesting.NewTestContext(t)
|
||||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodDisruptionConditions, test.enablePodDisruptionConditions)
|
|
||||||
creationTime := time.Unix(0, 0)
|
creationTime := time.Unix(0, 0)
|
||||||
|
|
||||||
pods := make([]*v1.Pod, 0, len(test.pods))
|
pods := make([]*v1.Pod, 0, len(test.pods))
|
||||||
@ -505,12 +483,11 @@ func TestGCTerminating(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
pods []nameToPodConfig
|
pods []nameToPodConfig
|
||||||
nodes []node
|
nodes []node
|
||||||
deletedPodNames sets.Set[string]
|
deletedPodNames sets.Set[string]
|
||||||
patchedPodNames sets.Set[string]
|
patchedPodNames sets.Set[string]
|
||||||
enablePodDisruptionConditions bool
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "pods have deletion timestamp set and the corresponding nodes are not ready",
|
name: "pods have deletion timestamp set and the corresponding nodes are not ready",
|
||||||
@ -592,7 +569,7 @@ func TestGCTerminating(t *testing.T) {
|
|||||||
patchedPodNames: sets.New("b1", "b4", "b5", "b6"),
|
patchedPodNames: sets.New("b1", "b4", "b5", "b6"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "pods deleted from node tained out-of-service; PodDisruptionConditions enabled",
|
name: "pods deleted from node tainted out-of-service",
|
||||||
nodes: []node{
|
nodes: []node{
|
||||||
{name: "worker", readyCondition: v1.ConditionFalse, taints: []v1.Taint{{Key: v1.TaintNodeOutOfService,
|
{name: "worker", readyCondition: v1.ConditionFalse, taints: []v1.Taint{{Key: v1.TaintNodeOutOfService,
|
||||||
Effect: v1.TaintEffectNoExecute}}},
|
Effect: v1.TaintEffectNoExecute}}},
|
||||||
@ -602,16 +579,14 @@ func TestGCTerminating(t *testing.T) {
|
|||||||
{name: "b", phase: v1.PodFailed, deletionTimeStamp: &metav1.Time{}, nodeName: "worker"},
|
{name: "b", phase: v1.PodFailed, deletionTimeStamp: &metav1.Time{}, nodeName: "worker"},
|
||||||
{name: "c", phase: v1.PodSucceeded, deletionTimeStamp: &metav1.Time{}, nodeName: "worker"},
|
{name: "c", phase: v1.PodSucceeded, deletionTimeStamp: &metav1.Time{}, nodeName: "worker"},
|
||||||
},
|
},
|
||||||
deletedPodNames: sets.New("a", "b", "c"),
|
deletedPodNames: sets.New("a", "b", "c"),
|
||||||
patchedPodNames: sets.New("a"),
|
patchedPodNames: sets.New("a"),
|
||||||
enablePodDisruptionConditions: true,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, test := range testCases {
|
for _, test := range testCases {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
resetMetrics()
|
resetMetrics()
|
||||||
_, ctx := ktesting.NewTestContext(t)
|
_, ctx := ktesting.NewTestContext(t)
|
||||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodDisruptionConditions, test.enablePodDisruptionConditions)
|
|
||||||
|
|
||||||
creationTime := time.Unix(0, 0)
|
creationTime := time.Unix(0, 0)
|
||||||
nodes := make([]*v1.Node, 0, len(test.nodes))
|
nodes := make([]*v1.Node, 0, len(test.nodes))
|
||||||
@ -720,7 +695,6 @@ func TestGCInspectingPatchedPodBeforeDeletion(t *testing.T) {
|
|||||||
for _, test := range testCases {
|
for _, test := range testCases {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
_, ctx := ktesting.NewTestContext(t)
|
_, ctx := ktesting.NewTestContext(t)
|
||||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodDisruptionConditions, true)
|
|
||||||
|
|
||||||
pods := []*v1.Pod{test.pod}
|
pods := []*v1.Pod{test.pod}
|
||||||
|
|
||||||
|
@ -32,14 +32,11 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
"k8s.io/apiserver/pkg/util/feature"
|
|
||||||
"k8s.io/client-go/informers"
|
"k8s.io/client-go/informers"
|
||||||
"k8s.io/client-go/kubernetes/fake"
|
"k8s.io/client-go/kubernetes/fake"
|
||||||
clienttesting "k8s.io/client-go/testing"
|
clienttesting "k8s.io/client-go/testing"
|
||||||
"k8s.io/client-go/tools/cache"
|
"k8s.io/client-go/tools/cache"
|
||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
|
||||||
"k8s.io/kubernetes/pkg/controller/testutil"
|
"k8s.io/kubernetes/pkg/controller/testutil"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var timeForControllerToProgressForSanityCheck = 20 * time.Millisecond
|
var timeForControllerToProgressForSanityCheck = 20 * time.Millisecond
|
||||||
@ -139,12 +136,11 @@ func TestFilterNoExecuteTaints(t *testing.T) {
|
|||||||
|
|
||||||
func TestCreatePod(t *testing.T) {
|
func TestCreatePod(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
description string
|
description string
|
||||||
pod *corev1.Pod
|
pod *corev1.Pod
|
||||||
taintedNodes map[string][]corev1.Taint
|
taintedNodes map[string][]corev1.Taint
|
||||||
expectPatch bool
|
expectPatch bool
|
||||||
expectDelete bool
|
expectDelete bool
|
||||||
enablePodDisruptionConditions bool
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
description: "not scheduled - ignore",
|
description: "not scheduled - ignore",
|
||||||
@ -164,18 +160,9 @@ func TestCreatePod(t *testing.T) {
|
|||||||
taintedNodes: map[string][]corev1.Taint{
|
taintedNodes: map[string][]corev1.Taint{
|
||||||
"node1": {createNoExecuteTaint(1)},
|
"node1": {createNoExecuteTaint(1)},
|
||||||
},
|
},
|
||||||
|
expectPatch: true,
|
||||||
expectDelete: true,
|
expectDelete: true,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
description: "schedule on tainted Node; PodDisruptionConditions enabled",
|
|
||||||
pod: testutil.NewPod("pod1", "node1"),
|
|
||||||
taintedNodes: map[string][]corev1.Taint{
|
|
||||||
"node1": {createNoExecuteTaint(1)},
|
|
||||||
},
|
|
||||||
expectPatch: true,
|
|
||||||
expectDelete: true,
|
|
||||||
enablePodDisruptionConditions: true,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
description: "schedule on tainted Node with finite toleration",
|
description: "schedule on tainted Node with finite toleration",
|
||||||
pod: addToleration(testutil.NewPod("pod1", "node1"), 1, 100),
|
pod: addToleration(testutil.NewPod("pod1", "node1"), 1, 100),
|
||||||
@ -193,18 +180,18 @@ func TestCreatePod(t *testing.T) {
|
|||||||
expectDelete: false,
|
expectDelete: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "schedule on tainted Node with infinite ivalid toleration",
|
description: "schedule on tainted Node with infinite invalid toleration",
|
||||||
pod: addToleration(testutil.NewPod("pod1", "node1"), 2, -1),
|
pod: addToleration(testutil.NewPod("pod1", "node1"), 2, -1),
|
||||||
taintedNodes: map[string][]corev1.Taint{
|
taintedNodes: map[string][]corev1.Taint{
|
||||||
"node1": {createNoExecuteTaint(1)},
|
"node1": {createNoExecuteTaint(1)},
|
||||||
},
|
},
|
||||||
|
expectPatch: true,
|
||||||
expectDelete: true,
|
expectDelete: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, item := range testCases {
|
for _, item := range testCases {
|
||||||
t.Run(item.description, func(t *testing.T) {
|
t.Run(item.description, func(t *testing.T) {
|
||||||
featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.PodDisruptionConditions, item.enablePodDisruptionConditions)
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
fakeClientset := fake.NewSimpleClientset(&corev1.PodList{Items: []corev1.Pod{*item.pod}})
|
fakeClientset := fake.NewSimpleClientset(&corev1.PodList{Items: []corev1.Pod{*item.pod}})
|
||||||
controller, podIndexer, _ := setupNewController(ctx, fakeClientset)
|
controller, podIndexer, _ := setupNewController(ctx, fakeClientset)
|
||||||
@ -240,27 +227,15 @@ func TestDeletePod(t *testing.T) {
|
|||||||
|
|
||||||
func TestUpdatePod(t *testing.T) {
|
func TestUpdatePod(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
description string
|
description string
|
||||||
prevPod *corev1.Pod
|
prevPod *corev1.Pod
|
||||||
awaitForScheduledEviction bool
|
awaitForScheduledEviction bool
|
||||||
newPod *corev1.Pod
|
newPod *corev1.Pod
|
||||||
taintedNodes map[string][]corev1.Taint
|
taintedNodes map[string][]corev1.Taint
|
||||||
expectPatch bool
|
expectPatch bool
|
||||||
expectDelete bool
|
expectDelete bool
|
||||||
enablePodDisruptionConditions bool
|
skipOnWindows bool
|
||||||
skipOnWindows bool
|
|
||||||
}{
|
}{
|
||||||
{
|
|
||||||
description: "scheduling onto tainted Node results in patch and delete when PodDisruptionConditions enabled",
|
|
||||||
prevPod: testutil.NewPod("pod1", ""),
|
|
||||||
newPod: testutil.NewPod("pod1", "node1"),
|
|
||||||
taintedNodes: map[string][]corev1.Taint{
|
|
||||||
"node1": {createNoExecuteTaint(1)},
|
|
||||||
},
|
|
||||||
expectPatch: true,
|
|
||||||
expectDelete: true,
|
|
||||||
enablePodDisruptionConditions: true,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
description: "scheduling onto tainted Node",
|
description: "scheduling onto tainted Node",
|
||||||
prevPod: testutil.NewPod("pod1", ""),
|
prevPod: testutil.NewPod("pod1", ""),
|
||||||
@ -268,6 +243,7 @@ func TestUpdatePod(t *testing.T) {
|
|||||||
taintedNodes: map[string][]corev1.Taint{
|
taintedNodes: map[string][]corev1.Taint{
|
||||||
"node1": {createNoExecuteTaint(1)},
|
"node1": {createNoExecuteTaint(1)},
|
||||||
},
|
},
|
||||||
|
expectPatch: true,
|
||||||
expectDelete: true,
|
expectDelete: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -287,6 +263,7 @@ func TestUpdatePod(t *testing.T) {
|
|||||||
taintedNodes: map[string][]corev1.Taint{
|
taintedNodes: map[string][]corev1.Taint{
|
||||||
"node1": {createNoExecuteTaint(1)},
|
"node1": {createNoExecuteTaint(1)},
|
||||||
},
|
},
|
||||||
|
expectPatch: true,
|
||||||
expectDelete: true,
|
expectDelete: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -297,6 +274,7 @@ func TestUpdatePod(t *testing.T) {
|
|||||||
taintedNodes: map[string][]corev1.Taint{
|
taintedNodes: map[string][]corev1.Taint{
|
||||||
"node1": {createNoExecuteTaint(1)},
|
"node1": {createNoExecuteTaint(1)},
|
||||||
},
|
},
|
||||||
|
expectPatch: true,
|
||||||
expectDelete: true,
|
expectDelete: true,
|
||||||
skipOnWindows: true,
|
skipOnWindows: true,
|
||||||
},
|
},
|
||||||
@ -308,7 +286,6 @@ func TestUpdatePod(t *testing.T) {
|
|||||||
// TODO: remove skip once the flaking test has been fixed.
|
// TODO: remove skip once the flaking test has been fixed.
|
||||||
t.Skip("Skip flaking test on Windows.")
|
t.Skip("Skip flaking test on Windows.")
|
||||||
}
|
}
|
||||||
featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.PodDisruptionConditions, item.enablePodDisruptionConditions)
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
fakeClientset := fake.NewSimpleClientset(&corev1.PodList{Items: []corev1.Pod{*item.prevPod}})
|
fakeClientset := fake.NewSimpleClientset(&corev1.PodList{Items: []corev1.Pod{*item.prevPod}})
|
||||||
controller, podIndexer, _ := setupNewController(context.TODO(), fakeClientset)
|
controller, podIndexer, _ := setupNewController(context.TODO(), fakeClientset)
|
||||||
@ -417,33 +394,22 @@ func TestDeleteNode(t *testing.T) {
|
|||||||
|
|
||||||
func TestUpdateNode(t *testing.T) {
|
func TestUpdateNode(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
description string
|
description string
|
||||||
pods []corev1.Pod
|
pods []corev1.Pod
|
||||||
oldNode *corev1.Node
|
oldNode *corev1.Node
|
||||||
newNode *corev1.Node
|
newNode *corev1.Node
|
||||||
expectPatch bool
|
expectPatch bool
|
||||||
expectDelete bool
|
expectDelete bool
|
||||||
additionalSleep time.Duration
|
additionalSleep time.Duration
|
||||||
enablePodDisruptionConditions bool
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
description: "Added taint, expect node patched and deleted when PodDisruptionConditions is enabled",
|
description: "Added taint, expect node patched and deleted",
|
||||||
pods: []corev1.Pod{
|
|
||||||
*testutil.NewPod("pod1", "node1"),
|
|
||||||
},
|
|
||||||
oldNode: testutil.NewNode("node1"),
|
|
||||||
newNode: addTaintsToNode(testutil.NewNode("node1"), "testTaint1", "taint1", []int{1}),
|
|
||||||
expectPatch: true,
|
|
||||||
expectDelete: true,
|
|
||||||
enablePodDisruptionConditions: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "Added taint",
|
|
||||||
pods: []corev1.Pod{
|
pods: []corev1.Pod{
|
||||||
*testutil.NewPod("pod1", "node1"),
|
*testutil.NewPod("pod1", "node1"),
|
||||||
},
|
},
|
||||||
oldNode: testutil.NewNode("node1"),
|
oldNode: testutil.NewNode("node1"),
|
||||||
newNode: addTaintsToNode(testutil.NewNode("node1"), "testTaint1", "taint1", []int{1}),
|
newNode: addTaintsToNode(testutil.NewNode("node1"), "testTaint1", "taint1", []int{1}),
|
||||||
|
expectPatch: true,
|
||||||
expectDelete: true,
|
expectDelete: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -462,6 +428,7 @@ func TestUpdateNode(t *testing.T) {
|
|||||||
},
|
},
|
||||||
oldNode: testutil.NewNode("node1"),
|
oldNode: testutil.NewNode("node1"),
|
||||||
newNode: addTaintsToNode(testutil.NewNode("node1"), "testTaint1", "taint1", []int{1, 2}),
|
newNode: addTaintsToNode(testutil.NewNode("node1"), "testTaint1", "taint1", []int{1, 2}),
|
||||||
|
expectPatch: true,
|
||||||
expectDelete: true,
|
expectDelete: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -501,13 +468,13 @@ func TestUpdateNode(t *testing.T) {
|
|||||||
},
|
},
|
||||||
oldNode: testutil.NewNode("node1"),
|
oldNode: testutil.NewNode("node1"),
|
||||||
newNode: addTaintsToNode(testutil.NewNode("node1"), "testTaint1", "taint1", []int{1, 2}),
|
newNode: addTaintsToNode(testutil.NewNode("node1"), "testTaint1", "taint1", []int{1, 2}),
|
||||||
|
expectPatch: true,
|
||||||
expectDelete: true,
|
expectDelete: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, item := range testCases {
|
for _, item := range testCases {
|
||||||
t.Run(item.description, func(t *testing.T) {
|
t.Run(item.description, func(t *testing.T) {
|
||||||
featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.PodDisruptionConditions, item.enablePodDisruptionConditions)
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
|
@ -567,6 +567,7 @@ const (
|
|||||||
// kep: https://kep.k8s.io/3329
|
// kep: https://kep.k8s.io/3329
|
||||||
// alpha: v1.25
|
// alpha: v1.25
|
||||||
// beta: v1.26
|
// beta: v1.26
|
||||||
|
// stable: v1.31
|
||||||
//
|
//
|
||||||
// Enables support for appending a dedicated pod condition indicating that
|
// Enables support for appending a dedicated pod condition indicating that
|
||||||
// the pod is being deleted due to a disruption.
|
// the pod is being deleted due to a disruption.
|
||||||
@ -1117,7 +1118,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
|
|||||||
|
|
||||||
PodDeletionCost: {Default: true, PreRelease: featuregate.Beta},
|
PodDeletionCost: {Default: true, PreRelease: featuregate.Beta},
|
||||||
|
|
||||||
PodDisruptionConditions: {Default: true, PreRelease: featuregate.Beta},
|
PodDisruptionConditions: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.33
|
||||||
|
|
||||||
PodReadyToStartContainersCondition: {Default: true, PreRelease: featuregate.Beta},
|
PodReadyToStartContainersCondition: {Default: true, PreRelease: featuregate.Beta},
|
||||||
|
|
||||||
|
@ -268,111 +268,87 @@ type podToMake struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMemoryPressure_VerifyPodStatus(t *testing.T) {
|
func TestMemoryPressure_VerifyPodStatus(t *testing.T) {
|
||||||
testCases := map[string]struct {
|
podMaker := makePodWithMemoryStats
|
||||||
wantPodStatus v1.PodStatus
|
summaryStatsMaker := makeMemoryStats
|
||||||
}{
|
podsToMake := []podToMake{
|
||||||
"eviction due to memory pressure; no image fs": {
|
{name: "below-requests", requests: newResourceList("", "1Gi", ""), limits: newResourceList("", "1Gi", ""), memoryWorkingSet: "900Mi"},
|
||||||
wantPodStatus: v1.PodStatus{
|
{name: "above-requests", requests: newResourceList("", "100Mi", ""), limits: newResourceList("", "1Gi", ""), memoryWorkingSet: "700Mi"},
|
||||||
Phase: v1.PodFailed,
|
}
|
||||||
Reason: "Evicted",
|
pods := []*v1.Pod{}
|
||||||
Message: "The node was low on resource: memory. Threshold quantity: 2Gi, available: 1500Mi. ",
|
podStats := map[*v1.Pod]statsapi.PodStats{}
|
||||||
},
|
for _, podToMake := range podsToMake {
|
||||||
},
|
pod, podStat := podMaker(podToMake.name, podToMake.priority, podToMake.requests, podToMake.limits, podToMake.memoryWorkingSet)
|
||||||
"eviction due to memory pressure; image fs": {
|
pods = append(pods, pod)
|
||||||
wantPodStatus: v1.PodStatus{
|
podStats[pod] = podStat
|
||||||
Phase: v1.PodFailed,
|
}
|
||||||
Reason: "Evicted",
|
activePodsFunc := func() []*v1.Pod {
|
||||||
Message: "The node was low on resource: memory. Threshold quantity: 2Gi, available: 1500Mi. ",
|
return pods
|
||||||
|
}
|
||||||
|
|
||||||
|
fakeClock := testingclock.NewFakeClock(time.Now())
|
||||||
|
podKiller := &mockPodKiller{}
|
||||||
|
diskInfoProvider := &mockDiskInfoProvider{dedicatedImageFs: ptr.To(false)}
|
||||||
|
diskGC := &mockDiskGC{err: nil}
|
||||||
|
nodeRef := &v1.ObjectReference{Kind: "Node", Name: "test", UID: types.UID("test"), Namespace: ""}
|
||||||
|
|
||||||
|
config := Config{
|
||||||
|
PressureTransitionPeriod: time.Minute * 5,
|
||||||
|
Thresholds: []evictionapi.Threshold{
|
||||||
|
{
|
||||||
|
Signal: evictionapi.SignalMemoryAvailable,
|
||||||
|
Operator: evictionapi.OpLessThan,
|
||||||
|
Value: evictionapi.ThresholdValue{
|
||||||
|
Quantity: quantityMustParse("2Gi"),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for name, tc := range testCases {
|
summaryProvider := &fakeSummaryProvider{result: summaryStatsMaker("1500Mi", podStats)}
|
||||||
for _, enablePodDisruptionConditions := range []bool{false, true} {
|
manager := &managerImpl{
|
||||||
t.Run(fmt.Sprintf("%s;PodDisruptionConditions=%v", name, enablePodDisruptionConditions), func(t *testing.T) {
|
clock: fakeClock,
|
||||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodDisruptionConditions, enablePodDisruptionConditions)
|
killPodFunc: podKiller.killPodNow,
|
||||||
|
imageGC: diskGC,
|
||||||
|
containerGC: diskGC,
|
||||||
|
config: config,
|
||||||
|
recorder: &record.FakeRecorder{},
|
||||||
|
summaryProvider: summaryProvider,
|
||||||
|
nodeRef: nodeRef,
|
||||||
|
nodeConditionsLastObservedAt: nodeConditionsObservedAt{},
|
||||||
|
thresholdsFirstObservedAt: thresholdsObservedAt{},
|
||||||
|
}
|
||||||
|
|
||||||
podMaker := makePodWithMemoryStats
|
// synchronize to detect the memory pressure
|
||||||
summaryStatsMaker := makeMemoryStats
|
_, err := manager.synchronize(diskInfoProvider, activePodsFunc)
|
||||||
podsToMake := []podToMake{
|
|
||||||
{name: "below-requests", requests: newResourceList("", "1Gi", ""), limits: newResourceList("", "1Gi", ""), memoryWorkingSet: "900Mi"},
|
|
||||||
{name: "above-requests", requests: newResourceList("", "100Mi", ""), limits: newResourceList("", "1Gi", ""), memoryWorkingSet: "700Mi"},
|
|
||||||
}
|
|
||||||
pods := []*v1.Pod{}
|
|
||||||
podStats := map[*v1.Pod]statsapi.PodStats{}
|
|
||||||
for _, podToMake := range podsToMake {
|
|
||||||
pod, podStat := podMaker(podToMake.name, podToMake.priority, podToMake.requests, podToMake.limits, podToMake.memoryWorkingSet)
|
|
||||||
pods = append(pods, pod)
|
|
||||||
podStats[pod] = podStat
|
|
||||||
}
|
|
||||||
activePodsFunc := func() []*v1.Pod {
|
|
||||||
return pods
|
|
||||||
}
|
|
||||||
|
|
||||||
fakeClock := testingclock.NewFakeClock(time.Now())
|
if err != nil {
|
||||||
podKiller := &mockPodKiller{}
|
t.Fatalf("Manager expects no error but got %v", err)
|
||||||
diskInfoProvider := &mockDiskInfoProvider{dedicatedImageFs: ptr.To(false)}
|
}
|
||||||
diskGC := &mockDiskGC{err: nil}
|
// verify memory pressure is detected
|
||||||
nodeRef := &v1.ObjectReference{Kind: "Node", Name: "test", UID: types.UID("test"), Namespace: ""}
|
if !manager.IsUnderMemoryPressure() {
|
||||||
|
t.Fatalf("Manager should have detected memory pressure")
|
||||||
|
}
|
||||||
|
|
||||||
config := Config{
|
// verify a pod is selected for eviction
|
||||||
PressureTransitionPeriod: time.Minute * 5,
|
if podKiller.pod == nil {
|
||||||
Thresholds: []evictionapi.Threshold{
|
t.Fatalf("Manager should have selected a pod for eviction")
|
||||||
{
|
}
|
||||||
Signal: evictionapi.SignalMemoryAvailable,
|
|
||||||
Operator: evictionapi.OpLessThan,
|
|
||||||
Value: evictionapi.ThresholdValue{
|
|
||||||
Quantity: quantityMustParse("2Gi"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
summaryProvider := &fakeSummaryProvider{result: summaryStatsMaker("1500Mi", podStats)}
|
|
||||||
manager := &managerImpl{
|
|
||||||
clock: fakeClock,
|
|
||||||
killPodFunc: podKiller.killPodNow,
|
|
||||||
imageGC: diskGC,
|
|
||||||
containerGC: diskGC,
|
|
||||||
config: config,
|
|
||||||
recorder: &record.FakeRecorder{},
|
|
||||||
summaryProvider: summaryProvider,
|
|
||||||
nodeRef: nodeRef,
|
|
||||||
nodeConditionsLastObservedAt: nodeConditionsObservedAt{},
|
|
||||||
thresholdsFirstObservedAt: thresholdsObservedAt{},
|
|
||||||
}
|
|
||||||
|
|
||||||
// synchronize to detect the memory pressure
|
wantPodStatus := v1.PodStatus{
|
||||||
_, err := manager.synchronize(diskInfoProvider, activePodsFunc)
|
Phase: v1.PodFailed,
|
||||||
|
Reason: "Evicted",
|
||||||
|
Message: "The node was low on resource: memory. Threshold quantity: 2Gi, available: 1500Mi. ",
|
||||||
|
Conditions: []v1.PodCondition{{
|
||||||
|
Type: "DisruptionTarget",
|
||||||
|
Status: "True",
|
||||||
|
Reason: "TerminationByKubelet",
|
||||||
|
Message: "The node was low on resource: memory. Threshold quantity: 2Gi, available: 1500Mi. ",
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
// verify the pod status after applying the status update function
|
||||||
t.Fatalf("Manager expects no error but got %v", err)
|
podKiller.statusFn(&podKiller.pod.Status)
|
||||||
}
|
if diff := cmp.Diff(wantPodStatus, podKiller.pod.Status, cmpopts.IgnoreFields(v1.PodCondition{}, "LastProbeTime", "LastTransitionTime")); diff != "" {
|
||||||
// verify memory pressure is detected
|
t.Errorf("Unexpected pod status of the evicted pod (-want,+got):\n%s", diff)
|
||||||
if !manager.IsUnderMemoryPressure() {
|
|
||||||
t.Fatalf("Manager should have detected memory pressure")
|
|
||||||
}
|
|
||||||
|
|
||||||
// verify a pod is selected for eviction
|
|
||||||
if podKiller.pod == nil {
|
|
||||||
t.Fatalf("Manager should have selected a pod for eviction")
|
|
||||||
}
|
|
||||||
|
|
||||||
wantPodStatus := tc.wantPodStatus.DeepCopy()
|
|
||||||
if enablePodDisruptionConditions {
|
|
||||||
wantPodStatus.Conditions = append(wantPodStatus.Conditions, v1.PodCondition{
|
|
||||||
Type: "DisruptionTarget",
|
|
||||||
Status: "True",
|
|
||||||
Reason: "TerminationByKubelet",
|
|
||||||
Message: "The node was low on resource: memory. Threshold quantity: 2Gi, available: 1500Mi. ",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// verify the pod status after applying the status update function
|
|
||||||
podKiller.statusFn(&podKiller.pod.Status)
|
|
||||||
if diff := cmp.Diff(*wantPodStatus, podKiller.pod.Status, cmpopts.IgnoreFields(v1.PodCondition{}, "LastProbeTime", "LastTransitionTime")); diff != "" {
|
|
||||||
t.Errorf("Unexpected pod status of the evicted pod (-want,+got):\n%s", diff)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -388,93 +364,85 @@ func TestPIDPressure_VerifyPodStatus(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for name, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
for _, enablePodDisruptionConditions := range []bool{true, false} {
|
podMaker := makePodWithPIDStats
|
||||||
t.Run(fmt.Sprintf("%s;PodDisruptionConditions=%v", name, enablePodDisruptionConditions), func(t *testing.T) {
|
summaryStatsMaker := makePIDStats
|
||||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodDisruptionConditions, enablePodDisruptionConditions)
|
podsToMake := []podToMake{
|
||||||
|
{name: "pod1", priority: lowPriority, pidUsage: 500},
|
||||||
|
{name: "pod2", priority: defaultPriority, pidUsage: 500},
|
||||||
|
}
|
||||||
|
pods := []*v1.Pod{}
|
||||||
|
podStats := map[*v1.Pod]statsapi.PodStats{}
|
||||||
|
for _, podToMake := range podsToMake {
|
||||||
|
pod, podStat := podMaker(podToMake.name, podToMake.priority, 2)
|
||||||
|
pods = append(pods, pod)
|
||||||
|
podStats[pod] = podStat
|
||||||
|
}
|
||||||
|
activePodsFunc := func() []*v1.Pod {
|
||||||
|
return pods
|
||||||
|
}
|
||||||
|
|
||||||
podMaker := makePodWithPIDStats
|
fakeClock := testingclock.NewFakeClock(time.Now())
|
||||||
summaryStatsMaker := makePIDStats
|
podKiller := &mockPodKiller{}
|
||||||
podsToMake := []podToMake{
|
diskInfoProvider := &mockDiskInfoProvider{dedicatedImageFs: ptr.To(false)}
|
||||||
{name: "pod1", priority: lowPriority, pidUsage: 500},
|
diskGC := &mockDiskGC{err: nil}
|
||||||
{name: "pod2", priority: defaultPriority, pidUsage: 500},
|
nodeRef := &v1.ObjectReference{Kind: "Node", Name: "test", UID: types.UID("test"), Namespace: ""}
|
||||||
}
|
|
||||||
pods := []*v1.Pod{}
|
|
||||||
podStats := map[*v1.Pod]statsapi.PodStats{}
|
|
||||||
for _, podToMake := range podsToMake {
|
|
||||||
pod, podStat := podMaker(podToMake.name, podToMake.priority, 2)
|
|
||||||
pods = append(pods, pod)
|
|
||||||
podStats[pod] = podStat
|
|
||||||
}
|
|
||||||
activePodsFunc := func() []*v1.Pod {
|
|
||||||
return pods
|
|
||||||
}
|
|
||||||
|
|
||||||
fakeClock := testingclock.NewFakeClock(time.Now())
|
config := Config{
|
||||||
podKiller := &mockPodKiller{}
|
PressureTransitionPeriod: time.Minute * 5,
|
||||||
diskInfoProvider := &mockDiskInfoProvider{dedicatedImageFs: ptr.To(false)}
|
Thresholds: []evictionapi.Threshold{
|
||||||
diskGC := &mockDiskGC{err: nil}
|
{
|
||||||
nodeRef := &v1.ObjectReference{Kind: "Node", Name: "test", UID: types.UID("test"), Namespace: ""}
|
Signal: evictionapi.SignalPIDAvailable,
|
||||||
|
Operator: evictionapi.OpLessThan,
|
||||||
config := Config{
|
Value: evictionapi.ThresholdValue{
|
||||||
PressureTransitionPeriod: time.Minute * 5,
|
Quantity: quantityMustParse("1200"),
|
||||||
Thresholds: []evictionapi.Threshold{
|
|
||||||
{
|
|
||||||
Signal: evictionapi.SignalPIDAvailable,
|
|
||||||
Operator: evictionapi.OpLessThan,
|
|
||||||
Value: evictionapi.ThresholdValue{
|
|
||||||
Quantity: quantityMustParse("1200"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
summaryProvider := &fakeSummaryProvider{result: summaryStatsMaker("1500", "1000", podStats)}
|
},
|
||||||
manager := &managerImpl{
|
}
|
||||||
clock: fakeClock,
|
summaryProvider := &fakeSummaryProvider{result: summaryStatsMaker("1500", "1000", podStats)}
|
||||||
killPodFunc: podKiller.killPodNow,
|
manager := &managerImpl{
|
||||||
imageGC: diskGC,
|
clock: fakeClock,
|
||||||
containerGC: diskGC,
|
killPodFunc: podKiller.killPodNow,
|
||||||
config: config,
|
imageGC: diskGC,
|
||||||
recorder: &record.FakeRecorder{},
|
containerGC: diskGC,
|
||||||
summaryProvider: summaryProvider,
|
config: config,
|
||||||
nodeRef: nodeRef,
|
recorder: &record.FakeRecorder{},
|
||||||
nodeConditionsLastObservedAt: nodeConditionsObservedAt{},
|
summaryProvider: summaryProvider,
|
||||||
thresholdsFirstObservedAt: thresholdsObservedAt{},
|
nodeRef: nodeRef,
|
||||||
}
|
nodeConditionsLastObservedAt: nodeConditionsObservedAt{},
|
||||||
|
thresholdsFirstObservedAt: thresholdsObservedAt{},
|
||||||
|
}
|
||||||
|
|
||||||
// synchronize to detect the PID pressure
|
// synchronize to detect the PID pressure
|
||||||
_, err := manager.synchronize(diskInfoProvider, activePodsFunc)
|
_, err := manager.synchronize(diskInfoProvider, activePodsFunc)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Manager expects no error but got %v", err)
|
t.Fatalf("Manager expects no error but got %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify PID pressure is detected
|
// verify PID pressure is detected
|
||||||
if !manager.IsUnderPIDPressure() {
|
if !manager.IsUnderPIDPressure() {
|
||||||
t.Fatalf("Manager should have detected PID pressure")
|
t.Fatalf("Manager should have detected PID pressure")
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify a pod is selected for eviction
|
// verify a pod is selected for eviction
|
||||||
if podKiller.pod == nil {
|
if podKiller.pod == nil {
|
||||||
t.Fatalf("Manager should have selected a pod for eviction")
|
t.Fatalf("Manager should have selected a pod for eviction")
|
||||||
}
|
}
|
||||||
|
|
||||||
wantPodStatus := tc.wantPodStatus.DeepCopy()
|
wantPodStatus := tc.wantPodStatus.DeepCopy()
|
||||||
if enablePodDisruptionConditions {
|
wantPodStatus.Conditions = append(wantPodStatus.Conditions, v1.PodCondition{
|
||||||
wantPodStatus.Conditions = append(wantPodStatus.Conditions, v1.PodCondition{
|
Type: "DisruptionTarget",
|
||||||
Type: "DisruptionTarget",
|
Status: "True",
|
||||||
Status: "True",
|
Reason: "TerminationByKubelet",
|
||||||
Reason: "TerminationByKubelet",
|
Message: "The node was low on resource: pids. Threshold quantity: 1200, available: 500. ",
|
||||||
Message: "The node was low on resource: pids. Threshold quantity: 1200, available: 500. ",
|
})
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// verify the pod status after applying the status update function
|
// verify the pod status after applying the status update function
|
||||||
podKiller.statusFn(&podKiller.pod.Status)
|
podKiller.statusFn(&podKiller.pod.Status)
|
||||||
if diff := cmp.Diff(*wantPodStatus, podKiller.pod.Status, cmpopts.IgnoreFields(v1.PodCondition{}, "LastProbeTime", "LastTransitionTime")); diff != "" {
|
if diff := cmp.Diff(*wantPodStatus, podKiller.pod.Status, cmpopts.IgnoreFields(v1.PodCondition{}, "LastProbeTime", "LastTransitionTime")); diff != "" {
|
||||||
t.Errorf("Unexpected pod status of the evicted pod (-want,+got):\n%s", diff)
|
t.Errorf("Unexpected pod status of the evicted pod (-want,+got):\n%s", diff)
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -570,97 +538,90 @@ func TestDiskPressureNodeFs_VerifyPodStatus(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for name, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
for _, enablePodDisruptionConditions := range []bool{false, true} {
|
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.KubeletSeparateDiskGC, tc.kubeletSeparateDiskFeature)
|
||||||
t.Run(fmt.Sprintf("%s;PodDisruptionConditions=%v", name, enablePodDisruptionConditions), func(t *testing.T) {
|
|
||||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.KubeletSeparateDiskGC, tc.kubeletSeparateDiskFeature)
|
|
||||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodDisruptionConditions, enablePodDisruptionConditions)
|
|
||||||
|
|
||||||
podMaker := makePodWithDiskStats
|
podMaker := makePodWithDiskStats
|
||||||
summaryStatsMaker := makeDiskStats
|
summaryStatsMaker := makeDiskStats
|
||||||
podsToMake := tc.podToMakes
|
podsToMake := tc.podToMakes
|
||||||
wantPodStatus := v1.PodStatus{
|
wantPodStatus := v1.PodStatus{
|
||||||
Phase: v1.PodFailed,
|
Phase: v1.PodFailed,
|
||||||
Reason: "Evicted",
|
Reason: "Evicted",
|
||||||
Message: tc.evictionMessage,
|
Message: tc.evictionMessage,
|
||||||
}
|
}
|
||||||
pods := []*v1.Pod{}
|
pods := []*v1.Pod{}
|
||||||
podStats := map[*v1.Pod]statsapi.PodStats{}
|
podStats := map[*v1.Pod]statsapi.PodStats{}
|
||||||
for _, podToMake := range podsToMake {
|
for _, podToMake := range podsToMake {
|
||||||
pod, podStat := podMaker(podToMake.name, podToMake.priority, podToMake.requests, podToMake.limits, podToMake.rootFsUsed, podToMake.logsFsUsed, podToMake.perLocalVolumeUsed, nil)
|
pod, podStat := podMaker(podToMake.name, podToMake.priority, podToMake.requests, podToMake.limits, podToMake.rootFsUsed, podToMake.logsFsUsed, podToMake.perLocalVolumeUsed, nil)
|
||||||
pods = append(pods, pod)
|
pods = append(pods, pod)
|
||||||
podStats[pod] = podStat
|
podStats[pod] = podStat
|
||||||
}
|
}
|
||||||
activePodsFunc := func() []*v1.Pod {
|
activePodsFunc := func() []*v1.Pod {
|
||||||
return pods
|
return pods
|
||||||
}
|
}
|
||||||
|
|
||||||
fakeClock := testingclock.NewFakeClock(time.Now())
|
fakeClock := testingclock.NewFakeClock(time.Now())
|
||||||
podKiller := &mockPodKiller{}
|
podKiller := &mockPodKiller{}
|
||||||
diskInfoProvider := &mockDiskInfoProvider{dedicatedImageFs: tc.dedicatedImageFs}
|
diskInfoProvider := &mockDiskInfoProvider{dedicatedImageFs: tc.dedicatedImageFs}
|
||||||
diskGC := &mockDiskGC{err: nil, readAndWriteSeparate: tc.writeableSeparateFromReadOnly}
|
diskGC := &mockDiskGC{err: nil, readAndWriteSeparate: tc.writeableSeparateFromReadOnly}
|
||||||
nodeRef := &v1.ObjectReference{Kind: "Node", Name: "test", UID: types.UID("test"), Namespace: ""}
|
nodeRef := &v1.ObjectReference{Kind: "Node", Name: "test", UID: types.UID("test"), Namespace: ""}
|
||||||
|
|
||||||
config := Config{
|
config := Config{
|
||||||
PressureTransitionPeriod: time.Minute * 5,
|
PressureTransitionPeriod: time.Minute * 5,
|
||||||
Thresholds: []evictionapi.Threshold{tc.thresholdToMonitor},
|
Thresholds: []evictionapi.Threshold{tc.thresholdToMonitor},
|
||||||
}
|
}
|
||||||
diskStat := diskStats{
|
diskStat := diskStats{
|
||||||
rootFsAvailableBytes: tc.nodeFsStats,
|
rootFsAvailableBytes: tc.nodeFsStats,
|
||||||
imageFsAvailableBytes: tc.imageFsStats,
|
imageFsAvailableBytes: tc.imageFsStats,
|
||||||
containerFsAvailableBytes: tc.containerFsStats,
|
containerFsAvailableBytes: tc.containerFsStats,
|
||||||
podStats: podStats,
|
podStats: podStats,
|
||||||
}
|
}
|
||||||
summaryProvider := &fakeSummaryProvider{result: summaryStatsMaker(diskStat)}
|
summaryProvider := &fakeSummaryProvider{result: summaryStatsMaker(diskStat)}
|
||||||
manager := &managerImpl{
|
manager := &managerImpl{
|
||||||
clock: fakeClock,
|
clock: fakeClock,
|
||||||
killPodFunc: podKiller.killPodNow,
|
killPodFunc: podKiller.killPodNow,
|
||||||
imageGC: diskGC,
|
imageGC: diskGC,
|
||||||
containerGC: diskGC,
|
containerGC: diskGC,
|
||||||
config: config,
|
config: config,
|
||||||
recorder: &record.FakeRecorder{},
|
recorder: &record.FakeRecorder{},
|
||||||
summaryProvider: summaryProvider,
|
summaryProvider: summaryProvider,
|
||||||
nodeRef: nodeRef,
|
nodeRef: nodeRef,
|
||||||
nodeConditionsLastObservedAt: nodeConditionsObservedAt{},
|
nodeConditionsLastObservedAt: nodeConditionsObservedAt{},
|
||||||
thresholdsFirstObservedAt: thresholdsObservedAt{},
|
thresholdsFirstObservedAt: thresholdsObservedAt{},
|
||||||
}
|
}
|
||||||
|
|
||||||
// synchronize
|
// synchronize
|
||||||
pods, synchErr := manager.synchronize(diskInfoProvider, activePodsFunc)
|
pods, synchErr := manager.synchronize(diskInfoProvider, activePodsFunc)
|
||||||
|
|
||||||
if synchErr == nil && tc.expectErr != "" {
|
if synchErr == nil && tc.expectErr != "" {
|
||||||
t.Fatalf("Manager should report error but did not")
|
t.Fatalf("Manager should report error but did not")
|
||||||
} else if tc.expectErr != "" && synchErr != nil {
|
} else if tc.expectErr != "" && synchErr != nil {
|
||||||
if diff := cmp.Diff(tc.expectErr, synchErr.Error()); diff != "" {
|
if diff := cmp.Diff(tc.expectErr, synchErr.Error()); diff != "" {
|
||||||
t.Errorf("Unexpected error (-want,+got):\n%s", diff)
|
t.Errorf("Unexpected error (-want,+got):\n%s", diff)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// verify manager detected disk pressure
|
// verify manager detected disk pressure
|
||||||
if !manager.IsUnderDiskPressure() {
|
if !manager.IsUnderDiskPressure() {
|
||||||
t.Fatalf("Manager should report disk pressure")
|
t.Fatalf("Manager should report disk pressure")
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify a pod is selected for eviction
|
// verify a pod is selected for eviction
|
||||||
if podKiller.pod == nil {
|
if podKiller.pod == nil {
|
||||||
t.Fatalf("Manager should have selected a pod for eviction")
|
t.Fatalf("Manager should have selected a pod for eviction")
|
||||||
}
|
}
|
||||||
|
|
||||||
if enablePodDisruptionConditions {
|
wantPodStatus.Conditions = append(wantPodStatus.Conditions, v1.PodCondition{
|
||||||
wantPodStatus.Conditions = append(wantPodStatus.Conditions, v1.PodCondition{
|
Type: "DisruptionTarget",
|
||||||
Type: "DisruptionTarget",
|
Status: "True",
|
||||||
Status: "True",
|
Reason: "TerminationByKubelet",
|
||||||
Reason: "TerminationByKubelet",
|
Message: tc.evictionMessage,
|
||||||
Message: tc.evictionMessage,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// verify the pod status after applying the status update function
|
|
||||||
podKiller.statusFn(&podKiller.pod.Status)
|
|
||||||
if diff := cmp.Diff(wantPodStatus, podKiller.pod.Status, cmpopts.IgnoreFields(v1.PodCondition{}, "LastProbeTime", "LastTransitionTime")); diff != "" {
|
|
||||||
t.Errorf("Unexpected pod status of the evicted pod (-want,+got):\n%s", diff)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// verify the pod status after applying the status update function
|
||||||
|
podKiller.statusFn(&podKiller.pod.Status)
|
||||||
|
if diff := cmp.Diff(wantPodStatus, podKiller.pod.Status, cmpopts.IgnoreFields(v1.PodCondition{}, "LastProbeTime", "LastTransitionTime")); diff != "" {
|
||||||
|
t.Errorf("Unexpected pod status of the evicted pod (-want,+got):\n%s", diff)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3267,13 +3267,12 @@ func Test_generateAPIPodStatus(t *testing.T) {
|
|||||||
unreadyContainer []string
|
unreadyContainer []string
|
||||||
previousStatus v1.PodStatus
|
previousStatus v1.PodStatus
|
||||||
isPodTerminal bool
|
isPodTerminal bool
|
||||||
enablePodDisruptionConditions bool
|
|
||||||
expected v1.PodStatus
|
expected v1.PodStatus
|
||||||
expectedPodDisruptionCondition v1.PodCondition
|
expectedPodDisruptionCondition *v1.PodCondition
|
||||||
expectedPodReadyToStartContainersCondition v1.PodCondition
|
expectedPodReadyToStartContainersCondition v1.PodCondition
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "pod disruption condition is copied over and the phase is set to failed when deleted; PodDisruptionConditions enabled",
|
name: "pod disruption condition is copied over and the phase is set to failed when deleted",
|
||||||
pod: &v1.Pod{
|
pod: &v1.Pod{
|
||||||
Spec: desiredState,
|
Spec: desiredState,
|
||||||
Status: v1.PodStatus{
|
Status: v1.PodStatus{
|
||||||
@ -3301,8 +3300,7 @@ func Test_generateAPIPodStatus(t *testing.T) {
|
|||||||
LastTransitionTime: normalized_now,
|
LastTransitionTime: normalized_now,
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
isPodTerminal: true,
|
isPodTerminal: true,
|
||||||
enablePodDisruptionConditions: true,
|
|
||||||
expected: v1.PodStatus{
|
expected: v1.PodStatus{
|
||||||
Phase: v1.PodFailed,
|
Phase: v1.PodFailed,
|
||||||
HostIP: "127.0.0.1",
|
HostIP: "127.0.0.1",
|
||||||
@ -3319,7 +3317,7 @@ func Test_generateAPIPodStatus(t *testing.T) {
|
|||||||
ready(waitingWithLastTerminationUnknown("containerB", 0)),
|
ready(waitingWithLastTerminationUnknown("containerB", 0)),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedPodDisruptionCondition: v1.PodCondition{
|
expectedPodDisruptionCondition: &v1.PodCondition{
|
||||||
Type: v1.DisruptionTarget,
|
Type: v1.DisruptionTarget,
|
||||||
Status: v1.ConditionTrue,
|
Status: v1.ConditionTrue,
|
||||||
LastTransitionTime: normalized_now,
|
LastTransitionTime: normalized_now,
|
||||||
@ -3705,7 +3703,6 @@ func Test_generateAPIPodStatus(t *testing.T) {
|
|||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
for _, enablePodReadyToStartContainersCondition := range []bool{false, true} {
|
for _, enablePodReadyToStartContainersCondition := range []bool{false, true} {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodDisruptionConditions, test.enablePodDisruptionConditions)
|
|
||||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodReadyToStartContainersCondition, enablePodReadyToStartContainersCondition)
|
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodReadyToStartContainersCondition, enablePodReadyToStartContainersCondition)
|
||||||
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
|
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
|
||||||
defer testKubelet.Cleanup()
|
defer testKubelet.Cleanup()
|
||||||
@ -3719,8 +3716,8 @@ func Test_generateAPIPodStatus(t *testing.T) {
|
|||||||
if enablePodReadyToStartContainersCondition {
|
if enablePodReadyToStartContainersCondition {
|
||||||
expected.Conditions = append([]v1.PodCondition{test.expectedPodReadyToStartContainersCondition}, expected.Conditions...)
|
expected.Conditions = append([]v1.PodCondition{test.expectedPodReadyToStartContainersCondition}, expected.Conditions...)
|
||||||
}
|
}
|
||||||
if test.enablePodDisruptionConditions {
|
if test.expectedPodDisruptionCondition != nil {
|
||||||
expected.Conditions = append([]v1.PodCondition{test.expectedPodDisruptionCondition}, expected.Conditions...)
|
expected.Conditions = append([]v1.PodCondition{*test.expectedPodDisruptionCondition}, expected.Conditions...)
|
||||||
}
|
}
|
||||||
if !apiequality.Semantic.DeepEqual(*expected, actual) {
|
if !apiequality.Semantic.DeepEqual(*expected, actual) {
|
||||||
t.Fatalf("Unexpected status: %s", cmp.Diff(*expected, actual))
|
t.Fatalf("Unexpected status: %s", cmp.Diff(*expected, actual))
|
||||||
|
@ -125,14 +125,13 @@ func TestManager(t *testing.T) {
|
|||||||
shutdownGracePeriodCriticalPods time.Duration
|
shutdownGracePeriodCriticalPods time.Duration
|
||||||
systemInhibitDelay time.Duration
|
systemInhibitDelay time.Duration
|
||||||
overrideSystemInhibitDelay time.Duration
|
overrideSystemInhibitDelay time.Duration
|
||||||
enablePodDisruptionConditions bool
|
|
||||||
expectedDidOverrideInhibitDelay bool
|
expectedDidOverrideInhibitDelay bool
|
||||||
expectedPodToGracePeriodOverride map[string]int64
|
expectedPodToGracePeriodOverride map[string]int64
|
||||||
expectedError error
|
expectedError error
|
||||||
expectedPodStatuses map[string]v1.PodStatus
|
expectedPodStatuses map[string]v1.PodStatus
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
desc: "verify pod status; PodDisruptionConditions enabled",
|
desc: "verify pod status",
|
||||||
activePods: []*v1.Pod{
|
activePods: []*v1.Pod{
|
||||||
{
|
{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "running-pod"},
|
ObjectMeta: metav1.ObjectMeta{Name: "running-pod"},
|
||||||
@ -160,7 +159,6 @@ func TestManager(t *testing.T) {
|
|||||||
shutdownGracePeriodCriticalPods: time.Duration(10 * time.Second),
|
shutdownGracePeriodCriticalPods: time.Duration(10 * time.Second),
|
||||||
systemInhibitDelay: time.Duration(40 * time.Second),
|
systemInhibitDelay: time.Duration(40 * time.Second),
|
||||||
overrideSystemInhibitDelay: time.Duration(40 * time.Second),
|
overrideSystemInhibitDelay: time.Duration(40 * time.Second),
|
||||||
enablePodDisruptionConditions: true,
|
|
||||||
expectedDidOverrideInhibitDelay: false,
|
expectedDidOverrideInhibitDelay: false,
|
||||||
expectedPodToGracePeriodOverride: map[string]int64{"running-pod": 20, "failed-pod": 20, "succeeded-pod": 20},
|
expectedPodToGracePeriodOverride: map[string]int64{"running-pod": 20, "failed-pod": 20, "succeeded-pod": 20},
|
||||||
expectedPodStatuses: map[string]v1.PodStatus{
|
expectedPodStatuses: map[string]v1.PodStatus{
|
||||||
@ -212,7 +210,6 @@ func TestManager(t *testing.T) {
|
|||||||
shutdownGracePeriodCriticalPods: time.Duration(10 * time.Second),
|
shutdownGracePeriodCriticalPods: time.Duration(10 * time.Second),
|
||||||
systemInhibitDelay: time.Duration(40 * time.Second),
|
systemInhibitDelay: time.Duration(40 * time.Second),
|
||||||
overrideSystemInhibitDelay: time.Duration(40 * time.Second),
|
overrideSystemInhibitDelay: time.Duration(40 * time.Second),
|
||||||
enablePodDisruptionConditions: false,
|
|
||||||
expectedDidOverrideInhibitDelay: false,
|
expectedDidOverrideInhibitDelay: false,
|
||||||
expectedPodToGracePeriodOverride: map[string]int64{"normal-pod-nil-grace-period": 20, "critical-pod-nil-grace-period": 10},
|
expectedPodToGracePeriodOverride: map[string]int64{"normal-pod-nil-grace-period": 20, "critical-pod-nil-grace-period": 10},
|
||||||
expectedPodStatuses: map[string]v1.PodStatus{
|
expectedPodStatuses: map[string]v1.PodStatus{
|
||||||
@ -220,11 +217,27 @@ func TestManager(t *testing.T) {
|
|||||||
Phase: v1.PodFailed,
|
Phase: v1.PodFailed,
|
||||||
Message: "Pod was terminated in response to imminent node shutdown.",
|
Message: "Pod was terminated in response to imminent node shutdown.",
|
||||||
Reason: "Terminated",
|
Reason: "Terminated",
|
||||||
|
Conditions: []v1.PodCondition{
|
||||||
|
{
|
||||||
|
Type: v1.DisruptionTarget,
|
||||||
|
Status: v1.ConditionTrue,
|
||||||
|
Reason: "TerminationByKubelet",
|
||||||
|
Message: "Pod was terminated in response to imminent node shutdown.",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"critical-pod-nil-grace-period": {
|
"critical-pod-nil-grace-period": {
|
||||||
Phase: v1.PodFailed,
|
Phase: v1.PodFailed,
|
||||||
Message: "Pod was terminated in response to imminent node shutdown.",
|
Message: "Pod was terminated in response to imminent node shutdown.",
|
||||||
Reason: "Terminated",
|
Reason: "Terminated",
|
||||||
|
Conditions: []v1.PodCondition{
|
||||||
|
{
|
||||||
|
Type: v1.DisruptionTarget,
|
||||||
|
Status: v1.ConditionTrue,
|
||||||
|
Reason: "TerminationByKubelet",
|
||||||
|
Message: "Pod was terminated in response to imminent node shutdown.",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -331,7 +344,6 @@ func TestManager(t *testing.T) {
|
|||||||
systemDbus = func() (dbusInhibiter, error) {
|
systemDbus = func() (dbusInhibiter, error) {
|
||||||
return fakeDbus, nil
|
return fakeDbus, nil
|
||||||
}
|
}
|
||||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, pkgfeatures.PodDisruptionConditions, tc.enablePodDisruptionConditions)
|
|
||||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, pkgfeatures.GracefulNodeShutdown, true)
|
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, pkgfeatures.GracefulNodeShutdown, true)
|
||||||
|
|
||||||
proberManager := probetest.FakeManager{}
|
proberManager := probetest.FakeManager{}
|
||||||
|
@ -35,14 +35,11 @@ import (
|
|||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
clientset "k8s.io/client-go/kubernetes"
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/client-go/kubernetes/fake"
|
"k8s.io/client-go/kubernetes/fake"
|
||||||
core "k8s.io/client-go/testing"
|
core "k8s.io/client-go/testing"
|
||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
|
||||||
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
|
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
api "k8s.io/kubernetes/pkg/apis/core"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||||
kubepod "k8s.io/kubernetes/pkg/kubelet/pod"
|
kubepod "k8s.io/kubernetes/pkg/kubelet/pod"
|
||||||
statustest "k8s.io/kubernetes/pkg/kubelet/status/testing"
|
statustest "k8s.io/kubernetes/pkg/kubelet/status/testing"
|
||||||
@ -1065,9 +1062,8 @@ func TestTerminatePod_DefaultUnknownStatus(t *testing.T) {
|
|||||||
|
|
||||||
func TestTerminatePod_EnsurePodPhaseIsTerminal(t *testing.T) {
|
func TestTerminatePod_EnsurePodPhaseIsTerminal(t *testing.T) {
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
enablePodDisruptionConditions bool
|
status v1.PodStatus
|
||||||
status v1.PodStatus
|
wantStatus v1.PodStatus
|
||||||
wantStatus v1.PodStatus
|
|
||||||
}{
|
}{
|
||||||
"Pending pod": {
|
"Pending pod": {
|
||||||
status: v1.PodStatus{
|
status: v1.PodStatus{
|
||||||
@ -1542,24 +1538,21 @@ func deleteAction() core.DeleteAction {
|
|||||||
|
|
||||||
func TestMergePodStatus(t *testing.T) {
|
func TestMergePodStatus(t *testing.T) {
|
||||||
useCases := []struct {
|
useCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
enablePodDisruptionConditions bool
|
hasRunningContainers bool
|
||||||
hasRunningContainers bool
|
oldPodStatus func(input v1.PodStatus) v1.PodStatus
|
||||||
oldPodStatus func(input v1.PodStatus) v1.PodStatus
|
newPodStatus func(input v1.PodStatus) v1.PodStatus
|
||||||
newPodStatus func(input v1.PodStatus) v1.PodStatus
|
expectPodStatus v1.PodStatus
|
||||||
expectPodStatus v1.PodStatus
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"no change",
|
"no change",
|
||||||
false,
|
false,
|
||||||
false,
|
|
||||||
func(input v1.PodStatus) v1.PodStatus { return input },
|
func(input v1.PodStatus) v1.PodStatus { return input },
|
||||||
func(input v1.PodStatus) v1.PodStatus { return input },
|
func(input v1.PodStatus) v1.PodStatus { return input },
|
||||||
getPodStatus(),
|
getPodStatus(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"add DisruptionTarget condition when transitioning into failed phase; PodDisruptionConditions enabled",
|
"add DisruptionTarget condition when transitioning into failed phase",
|
||||||
true,
|
|
||||||
false,
|
false,
|
||||||
func(input v1.PodStatus) v1.PodStatus { return input },
|
func(input v1.PodStatus) v1.PodStatus { return input },
|
||||||
func(input v1.PodStatus) v1.PodStatus {
|
func(input v1.PodStatus) v1.PodStatus {
|
||||||
@ -1598,8 +1591,7 @@ func TestMergePodStatus(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"don't add DisruptionTarget condition when transitioning into failed phase, but there are might still be running containers; PodDisruptionConditions enabled",
|
"don't add DisruptionTarget condition when transitioning into failed phase, but there might still be running containers",
|
||||||
true,
|
|
||||||
true,
|
true,
|
||||||
func(input v1.PodStatus) v1.PodStatus { return input },
|
func(input v1.PodStatus) v1.PodStatus { return input },
|
||||||
func(input v1.PodStatus) v1.PodStatus {
|
func(input v1.PodStatus) v1.PodStatus {
|
||||||
@ -1627,8 +1619,7 @@ func TestMergePodStatus(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"preserve DisruptionTarget condition; PodDisruptionConditions enabled",
|
"preserve DisruptionTarget condition",
|
||||||
true,
|
|
||||||
false,
|
false,
|
||||||
func(input v1.PodStatus) v1.PodStatus {
|
func(input v1.PodStatus) v1.PodStatus {
|
||||||
input.Conditions = append(input.Conditions, v1.PodCondition{
|
input.Conditions = append(input.Conditions, v1.PodCondition{
|
||||||
@ -1662,43 +1653,7 @@ func TestMergePodStatus(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"preserve DisruptionTarget condition; PodDisruptionConditions disabled",
|
"override DisruptionTarget condition",
|
||||||
false,
|
|
||||||
false,
|
|
||||||
func(input v1.PodStatus) v1.PodStatus {
|
|
||||||
input.Conditions = append(input.Conditions, v1.PodCondition{
|
|
||||||
Type: v1.DisruptionTarget,
|
|
||||||
Status: v1.ConditionTrue,
|
|
||||||
Reason: "TerminationByKubelet",
|
|
||||||
})
|
|
||||||
return input
|
|
||||||
},
|
|
||||||
func(input v1.PodStatus) v1.PodStatus {
|
|
||||||
return input
|
|
||||||
},
|
|
||||||
v1.PodStatus{
|
|
||||||
Phase: v1.PodRunning,
|
|
||||||
Conditions: []v1.PodCondition{
|
|
||||||
{
|
|
||||||
Type: v1.PodReady,
|
|
||||||
Status: v1.ConditionTrue,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Type: v1.PodScheduled,
|
|
||||||
Status: v1.ConditionTrue,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Type: v1.DisruptionTarget,
|
|
||||||
Status: v1.ConditionTrue,
|
|
||||||
Reason: "TerminationByKubelet",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Message: "Message",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"override DisruptionTarget condition; PodDisruptionConditions enabled",
|
|
||||||
true,
|
|
||||||
false,
|
false,
|
||||||
func(input v1.PodStatus) v1.PodStatus {
|
func(input v1.PodStatus) v1.PodStatus {
|
||||||
input.Conditions = append(input.Conditions, v1.PodCondition{
|
input.Conditions = append(input.Conditions, v1.PodCondition{
|
||||||
@ -1744,8 +1699,7 @@ func TestMergePodStatus(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"don't override DisruptionTarget condition when remaining in running phase; PodDisruptionConditions enabled",
|
"don't override DisruptionTarget condition when remaining in running phase",
|
||||||
true,
|
|
||||||
false,
|
false,
|
||||||
func(input v1.PodStatus) v1.PodStatus {
|
func(input v1.PodStatus) v1.PodStatus {
|
||||||
input.Conditions = append(input.Conditions, v1.PodCondition{
|
input.Conditions = append(input.Conditions, v1.PodCondition{
|
||||||
@ -1784,8 +1738,7 @@ func TestMergePodStatus(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"don't override DisruptionTarget condition when transitioning to failed phase but there might still be running containers; PodDisruptionConditions enabled",
|
"don't override DisruptionTarget condition when transitioning to failed phase but there might still be running containers",
|
||||||
true,
|
|
||||||
true,
|
true,
|
||||||
func(input v1.PodStatus) v1.PodStatus {
|
func(input v1.PodStatus) v1.PodStatus {
|
||||||
input.Conditions = append(input.Conditions, v1.PodCondition{
|
input.Conditions = append(input.Conditions, v1.PodCondition{
|
||||||
@ -1827,7 +1780,6 @@ func TestMergePodStatus(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"readiness changes",
|
"readiness changes",
|
||||||
false,
|
false,
|
||||||
false,
|
|
||||||
func(input v1.PodStatus) v1.PodStatus { return input },
|
func(input v1.PodStatus) v1.PodStatus { return input },
|
||||||
func(input v1.PodStatus) v1.PodStatus {
|
func(input v1.PodStatus) v1.PodStatus {
|
||||||
input.Conditions[0].Status = v1.ConditionFalse
|
input.Conditions[0].Status = v1.ConditionFalse
|
||||||
@ -1851,7 +1803,6 @@ func TestMergePodStatus(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"additional pod condition",
|
"additional pod condition",
|
||||||
false,
|
false,
|
||||||
false,
|
|
||||||
func(input v1.PodStatus) v1.PodStatus {
|
func(input v1.PodStatus) v1.PodStatus {
|
||||||
input.Conditions = append(input.Conditions, v1.PodCondition{
|
input.Conditions = append(input.Conditions, v1.PodCondition{
|
||||||
Type: v1.PodConditionType("example.com/feature"),
|
Type: v1.PodConditionType("example.com/feature"),
|
||||||
@ -1882,7 +1833,6 @@ func TestMergePodStatus(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"additional pod condition and readiness changes",
|
"additional pod condition and readiness changes",
|
||||||
false,
|
false,
|
||||||
false,
|
|
||||||
func(input v1.PodStatus) v1.PodStatus {
|
func(input v1.PodStatus) v1.PodStatus {
|
||||||
input.Conditions = append(input.Conditions, v1.PodCondition{
|
input.Conditions = append(input.Conditions, v1.PodCondition{
|
||||||
Type: v1.PodConditionType("example.com/feature"),
|
Type: v1.PodConditionType("example.com/feature"),
|
||||||
@ -1916,7 +1866,6 @@ func TestMergePodStatus(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"additional pod condition changes",
|
"additional pod condition changes",
|
||||||
false,
|
false,
|
||||||
false,
|
|
||||||
func(input v1.PodStatus) v1.PodStatus {
|
func(input v1.PodStatus) v1.PodStatus {
|
||||||
input.Conditions = append(input.Conditions, v1.PodCondition{
|
input.Conditions = append(input.Conditions, v1.PodCondition{
|
||||||
Type: v1.PodConditionType("example.com/feature"),
|
Type: v1.PodConditionType("example.com/feature"),
|
||||||
@ -1953,7 +1902,6 @@ func TestMergePodStatus(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"phase is transitioning to failed and no containers running",
|
"phase is transitioning to failed and no containers running",
|
||||||
false,
|
false,
|
||||||
false,
|
|
||||||
func(input v1.PodStatus) v1.PodStatus {
|
func(input v1.PodStatus) v1.PodStatus {
|
||||||
input.Phase = v1.PodRunning
|
input.Phase = v1.PodRunning
|
||||||
input.Reason = "Unknown"
|
input.Reason = "Unknown"
|
||||||
@ -1990,7 +1938,6 @@ func TestMergePodStatus(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"phase is transitioning to failed and containers running",
|
"phase is transitioning to failed and containers running",
|
||||||
false,
|
|
||||||
true,
|
true,
|
||||||
func(input v1.PodStatus) v1.PodStatus {
|
func(input v1.PodStatus) v1.PodStatus {
|
||||||
input.Phase = v1.PodRunning
|
input.Phase = v1.PodRunning
|
||||||
@ -2024,7 +1971,6 @@ func TestMergePodStatus(t *testing.T) {
|
|||||||
|
|
||||||
for _, tc := range useCases {
|
for _, tc := range useCases {
|
||||||
t.Run(tc.desc, func(t *testing.T) {
|
t.Run(tc.desc, func(t *testing.T) {
|
||||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodDisruptionConditions, tc.enablePodDisruptionConditions)
|
|
||||||
output := mergePodStatus(tc.oldPodStatus(getPodStatus()), tc.newPodStatus(getPodStatus()), tc.hasRunningContainers)
|
output := mergePodStatus(tc.oldPodStatus(getPodStatus()), tc.newPodStatus(getPodStatus()), tc.hasRunningContainers)
|
||||||
if !conditionsEqual(output.Conditions, tc.expectPodStatus.Conditions) || !statusEqual(output, tc.expectPodStatus) {
|
if !conditionsEqual(output.Conditions, tc.expectPodStatus.Conditions) || !statusEqual(output, tc.expectPodStatus) {
|
||||||
t.Fatalf("unexpected output: %s", cmp.Diff(tc.expectPodStatus, output))
|
t.Fatalf("unexpected output: %s", cmp.Diff(tc.expectPodStatus, output))
|
||||||
|
@ -27,7 +27,6 @@ import (
|
|||||||
|
|
||||||
func TestPodConditionByKubelet(t *testing.T) {
|
func TestPodConditionByKubelet(t *testing.T) {
|
||||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodReadyToStartContainersCondition, true)
|
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodReadyToStartContainersCondition, true)
|
||||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodDisruptionConditions, true)
|
|
||||||
|
|
||||||
trueCases := []v1.PodConditionType{
|
trueCases := []v1.PodConditionType{
|
||||||
v1.PodScheduled,
|
v1.PodScheduled,
|
||||||
@ -56,8 +55,6 @@ func TestPodConditionByKubelet(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPodConditionSharedByKubelet(t *testing.T) {
|
func TestPodConditionSharedByKubelet(t *testing.T) {
|
||||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodDisruptionConditions, true)
|
|
||||||
|
|
||||||
trueCases := []v1.PodConditionType{
|
trueCases := []v1.PodConditionType{
|
||||||
v1.DisruptionTarget,
|
v1.DisruptionTarget,
|
||||||
}
|
}
|
||||||
|
@ -34,13 +34,10 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/watch"
|
"k8s.io/apimachinery/pkg/watch"
|
||||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||||
"k8s.io/apiserver/pkg/registry/rest"
|
"k8s.io/apiserver/pkg/registry/rest"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
"k8s.io/client-go/kubernetes/fake"
|
"k8s.io/client-go/kubernetes/fake"
|
||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
|
||||||
podapi "k8s.io/kubernetes/pkg/api/pod"
|
podapi "k8s.io/kubernetes/pkg/api/pod"
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
api "k8s.io/kubernetes/pkg/apis/core"
|
||||||
"k8s.io/kubernetes/pkg/apis/policy"
|
"k8s.io/kubernetes/pkg/apis/policy"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestEviction(t *testing.T) {
|
func TestEviction(t *testing.T) {
|
||||||
@ -802,46 +799,42 @@ func TestAddConditionAndDelete(t *testing.T) {
|
|||||||
evictionRest := newEvictionStorage(storage.Store, client.PolicyV1())
|
evictionRest := newEvictionStorage(storage.Store, client.PolicyV1())
|
||||||
|
|
||||||
for _, tc := range cases {
|
for _, tc := range cases {
|
||||||
for _, conditionsEnabled := range []bool{true, false} {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
name := fmt.Sprintf("%s_conditions=%v", tc.name, conditionsEnabled)
|
var deleteOptions *metav1.DeleteOptions
|
||||||
t.Run(name, func(t *testing.T) {
|
if tc.initialPod {
|
||||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodDisruptionConditions, conditionsEnabled)
|
newPod := validNewPod()
|
||||||
var deleteOptions *metav1.DeleteOptions
|
createdObj, err := storage.Create(testContext, newPod, rest.ValidateAllObjectFunc, &metav1.CreateOptions{})
|
||||||
if tc.initialPod {
|
if err != nil {
|
||||||
newPod := validNewPod()
|
t.Fatal(err)
|
||||||
createdObj, err := storage.Create(testContext, newPod, rest.ValidateAllObjectFunc, &metav1.CreateOptions{})
|
}
|
||||||
if err != nil {
|
t.Cleanup(func() {
|
||||||
|
zero := int64(0)
|
||||||
|
if _, _, err := storage.Delete(testContext, newPod.Name, rest.ValidateAllObjectFunc, &metav1.DeleteOptions{GracePeriodSeconds: &zero}); err != nil && !apierrors.IsNotFound(err) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
t.Cleanup(func() {
|
})
|
||||||
zero := int64(0)
|
deleteOptions = tc.makeDeleteOptions(createdObj.(*api.Pod))
|
||||||
if _, _, err := storage.Delete(testContext, newPod.Name, rest.ValidateAllObjectFunc, &metav1.DeleteOptions{GracePeriodSeconds: &zero}); err != nil && !apierrors.IsNotFound(err) {
|
} else {
|
||||||
t.Fatal(err)
|
deleteOptions = tc.makeDeleteOptions(nil)
|
||||||
}
|
}
|
||||||
})
|
if deleteOptions == nil {
|
||||||
deleteOptions = tc.makeDeleteOptions(createdObj.(*api.Pod))
|
deleteOptions = &metav1.DeleteOptions{}
|
||||||
} else {
|
}
|
||||||
deleteOptions = tc.makeDeleteOptions(nil)
|
|
||||||
}
|
|
||||||
if deleteOptions == nil {
|
|
||||||
deleteOptions = &metav1.DeleteOptions{}
|
|
||||||
}
|
|
||||||
|
|
||||||
err := addConditionAndDeletePod(evictionRest, testContext, "foo", rest.ValidateAllObjectFunc, deleteOptions)
|
err := addConditionAndDeletePod(evictionRest, testContext, "foo", rest.ValidateAllObjectFunc, deleteOptions)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if tc.expectErr != "" {
|
if tc.expectErr != "" {
|
||||||
t.Fatalf("expected err containing %q, got none", tc.expectErr)
|
t.Fatalf("expected err containing %q, got none", tc.expectErr)
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if tc.expectErr == "" {
|
return
|
||||||
t.Fatalf("unexpected err: %v", err)
|
}
|
||||||
}
|
if tc.expectErr == "" {
|
||||||
if !strings.Contains(err.Error(), tc.expectErr) {
|
t.Fatalf("unexpected err: %v", err)
|
||||||
t.Fatalf("expected err containing %q, got %v", tc.expectErr, err)
|
}
|
||||||
}
|
if !strings.Contains(err.Error(), tc.expectErr) {
|
||||||
})
|
t.Fatalf("expected err containing %q, got %v", tc.expectErr, err)
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1431,14 +1431,6 @@ func TestPodEligibleToPreemptOthers(t *testing.T) {
|
|||||||
nominatedNodeStatus: framework.NewStatus(framework.UnschedulableAndUnresolvable, tainttoleration.ErrReasonNotMatch),
|
nominatedNodeStatus: framework.NewStatus(framework.UnschedulableAndUnresolvable, tainttoleration.ErrReasonNotMatch),
|
||||||
expected: true,
|
expected: true,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "Pod with nominated node, but without nominated node status",
|
|
||||||
pod: st.MakePod().Name("p_without_status").UID("p").Priority(highPriority).NominatedNodeName("node1").Obj(),
|
|
||||||
pods: []*v1.Pod{st.MakePod().Name("p1").UID("p1").Priority(lowPriority).Node("node1").Terminating().Obj()},
|
|
||||||
nodes: []string{"node1"},
|
|
||||||
nominatedNodeStatus: nil,
|
|
||||||
expected: false,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "Pod without nominated node",
|
name: "Pod without nominated node",
|
||||||
pod: st.MakePod().Name("p_without_nominated_node").UID("p").Priority(highPriority).Obj(),
|
pod: st.MakePod().Name("p_without_nominated_node").UID("p").Priority(highPriority).Obj(),
|
||||||
@ -1456,8 +1448,7 @@ func TestPodEligibleToPreemptOthers(t *testing.T) {
|
|||||||
expected: false,
|
expected: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "victim Pods terminating, feature PodDisruptionConditions is enabled",
|
name: "preemption victim pod terminating, as indicated by the dedicated DisruptionTarget condition",
|
||||||
fts: feature.Features{EnablePodDisruptionConditions: true},
|
|
||||||
pod: st.MakePod().Name("p_with_nominated_node").UID("p").Priority(highPriority).NominatedNodeName("node1").Obj(),
|
pod: st.MakePod().Name("p_with_nominated_node").UID("p").Priority(highPriority).NominatedNodeName("node1").Obj(),
|
||||||
pods: []*v1.Pod{st.MakePod().Name("p1").UID("p1").Priority(lowPriority).Node("node1").Terminating().
|
pods: []*v1.Pod{st.MakePod().Name("p1").UID("p1").Priority(lowPriority).Node("node1").Terminating().
|
||||||
Condition(v1.DisruptionTarget, v1.ConditionTrue, v1.PodReasonPreemptionByScheduler).Obj()},
|
Condition(v1.DisruptionTarget, v1.ConditionTrue, v1.PodReasonPreemptionByScheduler).Obj()},
|
||||||
@ -1465,34 +1456,17 @@ func TestPodEligibleToPreemptOthers(t *testing.T) {
|
|||||||
expected: false,
|
expected: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "non-victim Pods terminating, feature PodDisruptionConditions is enabled",
|
name: "non-victim Pods terminating",
|
||||||
fts: feature.Features{EnablePodDisruptionConditions: true},
|
|
||||||
pod: st.MakePod().Name("p_with_nominated_node").UID("p").Priority(highPriority).NominatedNodeName("node1").Obj(),
|
pod: st.MakePod().Name("p_with_nominated_node").UID("p").Priority(highPriority).NominatedNodeName("node1").Obj(),
|
||||||
pods: []*v1.Pod{st.MakePod().Name("p1").UID("p1").Priority(lowPriority).Node("node1").Terminating().Obj()},
|
pods: []*v1.Pod{st.MakePod().Name("p1").UID("p1").Priority(lowPriority).Node("node1").Terminating().Obj()},
|
||||||
nodes: []string{"node1"},
|
nodes: []string{"node1"},
|
||||||
expected: true,
|
expected: true,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "victim Pods terminating, feature PodDisruptionConditions is disabled",
|
|
||||||
fts: feature.Features{EnablePodDisruptionConditions: false},
|
|
||||||
pod: st.MakePod().Name("p_with_nominated_node").UID("p").Priority(highPriority).NominatedNodeName("node1").Obj(),
|
|
||||||
pods: []*v1.Pod{st.MakePod().Name("p1").UID("p1").Priority(lowPriority).Node("node1").Terminating().
|
|
||||||
Condition(v1.DisruptionTarget, v1.ConditionTrue, v1.PodReasonPreemptionByScheduler).Obj()},
|
|
||||||
nodes: []string{"node1"},
|
|
||||||
expected: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "non-victim Pods terminating, feature PodDisruptionConditions is disabled",
|
|
||||||
fts: feature.Features{EnablePodDisruptionConditions: false},
|
|
||||||
pod: st.MakePod().Name("p_with_nominated_node").UID("p").Priority(highPriority).NominatedNodeName("node1").Obj(),
|
|
||||||
pods: []*v1.Pod{st.MakePod().Name("p1").UID("p1").Priority(lowPriority).Node("node1").Terminating().Obj()},
|
|
||||||
nodes: []string{"node1"},
|
|
||||||
expected: false,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
test.fts.EnablePodDisruptionConditions = true
|
||||||
logger, ctx := ktesting.NewTestContext(t)
|
logger, ctx := ktesting.NewTestContext(t)
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
@ -82,9 +82,6 @@ var (
|
|||||||
// TODO: document the feature (owning SIG, when to use this feature for a test)
|
// TODO: document the feature (owning SIG, when to use this feature for a test)
|
||||||
OOMScoreAdj = framework.WithNodeFeature(framework.ValidNodeFeatures.Add("OOMScoreAdj"))
|
OOMScoreAdj = framework.WithNodeFeature(framework.ValidNodeFeatures.Add("OOMScoreAdj"))
|
||||||
|
|
||||||
// TODO: document the feature (owning SIG, when to use this feature for a test)
|
|
||||||
PodDisruptionConditions = framework.WithNodeFeature(framework.ValidNodeFeatures.Add("PodDisruptionConditions"))
|
|
||||||
|
|
||||||
// TODO: document the feature (owning SIG, when to use this feature for a test)
|
// TODO: document the feature (owning SIG, when to use this feature for a test)
|
||||||
PodResources = framework.WithNodeFeature(framework.ValidNodeFeatures.Add("PodResources"))
|
PodResources = framework.WithNodeFeature(framework.ValidNodeFeatures.Add("PodResources"))
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ var _ = SIGDescribe("CriticalPod", framework.WithSerial(), framework.WithDisrupt
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
f.It("should add DisruptionTarget condition to the preempted pod", nodefeature.PodDisruptionConditions, func(ctx context.Context) {
|
f.It("should add DisruptionTarget condition to the preempted pod", func(ctx context.Context) {
|
||||||
// because adminssion Priority enable, If the priority class is not found, the Pod is rejected.
|
// because adminssion Priority enable, If the priority class is not found, the Pod is rejected.
|
||||||
node := getNodeName(ctx, f)
|
node := getNodeName(ctx, f)
|
||||||
nonCriticalGuaranteed := getTestPod(false, guaranteedPodName, v1.ResourceRequirements{
|
nonCriticalGuaranteed := getTestPod(false, guaranteedPodName, v1.ResourceRequirements{
|
||||||
|
@ -31,7 +31,6 @@ import (
|
|||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/fields"
|
"k8s.io/apimachinery/pkg/fields"
|
||||||
kubeletstatsv1alpha1 "k8s.io/kubelet/pkg/apis/stats/v1alpha1"
|
kubeletstatsv1alpha1 "k8s.io/kubelet/pkg/apis/stats/v1alpha1"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config"
|
kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/eviction"
|
"k8s.io/kubernetes/pkg/kubelet/eviction"
|
||||||
evictionapi "k8s.io/kubernetes/pkg/kubelet/eviction/api"
|
evictionapi "k8s.io/kubernetes/pkg/kubelet/eviction/api"
|
||||||
@ -45,6 +44,7 @@ import (
|
|||||||
testutils "k8s.io/kubernetes/test/utils"
|
testutils "k8s.io/kubernetes/test/utils"
|
||||||
imageutils "k8s.io/kubernetes/test/utils/image"
|
imageutils "k8s.io/kubernetes/test/utils/image"
|
||||||
admissionapi "k8s.io/pod-security-admission/api"
|
admissionapi "k8s.io/pod-security-admission/api"
|
||||||
|
"k8s.io/utils/ptr"
|
||||||
|
|
||||||
"github.com/onsi/ginkgo/v2"
|
"github.com/onsi/ginkgo/v2"
|
||||||
"github.com/onsi/gomega"
|
"github.com/onsi/gomega"
|
||||||
@ -513,23 +513,19 @@ var _ = SIGDescribe("PriorityPidEvictionOrdering", framework.WithSlow(), framewo
|
|||||||
runEvictionTest(f, pressureTimeout, expectedNodeCondition, expectedStarvedResource, logPidMetrics, specs)
|
runEvictionTest(f, pressureTimeout, expectedNodeCondition, expectedStarvedResource, logPidMetrics, specs)
|
||||||
})
|
})
|
||||||
|
|
||||||
f.Context(fmt.Sprintf(testContextFmt, expectedNodeCondition)+"; PodDisruptionConditions enabled", nodefeature.PodDisruptionConditions, func() {
|
f.Context(fmt.Sprintf(testContextFmt, expectedNodeCondition)+"; baseline scenario to verify DisruptionTarget is added", func() {
|
||||||
tempSetCurrentKubeletConfig(f, func(ctx context.Context, initialConfig *kubeletconfig.KubeletConfiguration) {
|
tempSetCurrentKubeletConfig(f, func(ctx context.Context, initialConfig *kubeletconfig.KubeletConfiguration) {
|
||||||
pidsConsumed := int64(10000)
|
pidsConsumed := int64(10000)
|
||||||
summary := eventuallyGetSummary(ctx)
|
summary := eventuallyGetSummary(ctx)
|
||||||
availablePids := *(summary.Node.Rlimit.MaxPID) - *(summary.Node.Rlimit.NumOfRunningProcesses)
|
availablePids := *(summary.Node.Rlimit.MaxPID) - *(summary.Node.Rlimit.NumOfRunningProcesses)
|
||||||
initialConfig.EvictionHard = map[string]string{string(evictionapi.SignalPIDAvailable): fmt.Sprintf("%d", availablePids-pidsConsumed)}
|
initialConfig.EvictionHard = map[string]string{string(evictionapi.SignalPIDAvailable): fmt.Sprintf("%d", availablePids-pidsConsumed)}
|
||||||
initialConfig.EvictionMinimumReclaim = map[string]string{}
|
initialConfig.EvictionMinimumReclaim = map[string]string{}
|
||||||
initialConfig.FeatureGates = map[string]bool{
|
|
||||||
string(features.PodDisruptionConditions): true,
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
disruptionTarget := v1.DisruptionTarget
|
|
||||||
specs := []podEvictSpec{
|
specs := []podEvictSpec{
|
||||||
{
|
{
|
||||||
evictionPriority: 1,
|
evictionPriority: 1,
|
||||||
pod: pidConsumingPod("fork-bomb-container", 30000),
|
pod: pidConsumingPod("fork-bomb-container", 30000),
|
||||||
wantPodDisruptionCondition: &disruptionTarget,
|
wantPodDisruptionCondition: ptr.To(v1.DisruptionTarget),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
runEvictionTest(f, pressureTimeout, expectedNodeCondition, expectedStarvedResource, logPidMetrics, specs)
|
runEvictionTest(f, pressureTimeout, expectedNodeCondition, expectedStarvedResource, logPidMetrics, specs)
|
||||||
|
@ -83,7 +83,7 @@ var _ = SIGDescribe("GracefulNodeShutdown", framework.WithSerial(), nodefeature.
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
f.Context("graceful node shutdown when PodDisruptionConditions are enabled", nodefeature.PodDisruptionConditions, func() {
|
f.Context("graceful node shutdown; baseline scenario to verify DisruptionTarget is added", func() {
|
||||||
|
|
||||||
const (
|
const (
|
||||||
pollInterval = 1 * time.Second
|
pollInterval = 1 * time.Second
|
||||||
@ -95,7 +95,6 @@ var _ = SIGDescribe("GracefulNodeShutdown", framework.WithSerial(), nodefeature.
|
|||||||
tempSetCurrentKubeletConfig(f, func(ctx context.Context, initialConfig *kubeletconfig.KubeletConfiguration) {
|
tempSetCurrentKubeletConfig(f, func(ctx context.Context, initialConfig *kubeletconfig.KubeletConfiguration) {
|
||||||
initialConfig.FeatureGates = map[string]bool{
|
initialConfig.FeatureGates = map[string]bool{
|
||||||
string(features.GracefulNodeShutdown): true,
|
string(features.GracefulNodeShutdown): true,
|
||||||
string(features.PodDisruptionConditions): true,
|
|
||||||
string(features.GracefulNodeShutdownBasedOnPodPriority): false,
|
string(features.GracefulNodeShutdownBasedOnPodPriority): false,
|
||||||
}
|
}
|
||||||
initialConfig.ShutdownGracePeriod = metav1.Duration{Duration: nodeShutdownGracePeriod}
|
initialConfig.ShutdownGracePeriod = metav1.Duration{Duration: nodeShutdownGracePeriod}
|
||||||
|
@ -40,7 +40,6 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/util/intstr"
|
"k8s.io/apimachinery/pkg/util/intstr"
|
||||||
"k8s.io/apimachinery/pkg/util/uuid"
|
"k8s.io/apimachinery/pkg/util/uuid"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
"k8s.io/apiserver/pkg/util/feature"
|
|
||||||
cacheddiscovery "k8s.io/client-go/discovery/cached/memory"
|
cacheddiscovery "k8s.io/client-go/discovery/cached/memory"
|
||||||
"k8s.io/client-go/dynamic"
|
"k8s.io/client-go/dynamic"
|
||||||
"k8s.io/client-go/informers"
|
"k8s.io/client-go/informers"
|
||||||
@ -50,12 +49,10 @@ import (
|
|||||||
"k8s.io/client-go/restmapper"
|
"k8s.io/client-go/restmapper"
|
||||||
"k8s.io/client-go/scale"
|
"k8s.io/client-go/scale"
|
||||||
"k8s.io/client-go/tools/cache"
|
"k8s.io/client-go/tools/cache"
|
||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
|
||||||
"k8s.io/klog/v2"
|
"k8s.io/klog/v2"
|
||||||
kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
|
kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
|
||||||
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
|
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
|
||||||
"k8s.io/kubernetes/pkg/controller/disruption"
|
"k8s.io/kubernetes/pkg/controller/disruption"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
"k8s.io/kubernetes/test/integration/framework"
|
"k8s.io/kubernetes/test/integration/framework"
|
||||||
"k8s.io/kubernetes/test/utils/ktesting"
|
"k8s.io/kubernetes/test/utils/ktesting"
|
||||||
)
|
)
|
||||||
@ -346,36 +343,22 @@ func TestEvictionVersions(t *testing.T) {
|
|||||||
// TestEvictionWithFinalizers tests eviction with the use of finalizers
|
// TestEvictionWithFinalizers tests eviction with the use of finalizers
|
||||||
func TestEvictionWithFinalizers(t *testing.T) {
|
func TestEvictionWithFinalizers(t *testing.T) {
|
||||||
cases := map[string]struct {
|
cases := map[string]struct {
|
||||||
enablePodDisruptionConditions bool
|
phase v1.PodPhase
|
||||||
phase v1.PodPhase
|
dryRun bool
|
||||||
dryRun bool
|
wantDisruptionTargetCond bool
|
||||||
wantDisruptionTargetCond bool
|
|
||||||
}{
|
}{
|
||||||
"terminal pod with PodDisruptionConditions enabled": {
|
"terminal pod": {
|
||||||
enablePodDisruptionConditions: true,
|
phase: v1.PodSucceeded,
|
||||||
phase: v1.PodSucceeded,
|
wantDisruptionTargetCond: true,
|
||||||
wantDisruptionTargetCond: true,
|
|
||||||
},
|
},
|
||||||
"terminal pod with PodDisruptionConditions disabled": {
|
"running pod": {
|
||||||
enablePodDisruptionConditions: false,
|
phase: v1.PodRunning,
|
||||||
phase: v1.PodSucceeded,
|
wantDisruptionTargetCond: true,
|
||||||
wantDisruptionTargetCond: false,
|
|
||||||
},
|
},
|
||||||
"running pod with PodDisruptionConditions enabled": {
|
"running pod should not update conditions in dry-run mode": {
|
||||||
enablePodDisruptionConditions: true,
|
phase: v1.PodRunning,
|
||||||
phase: v1.PodRunning,
|
dryRun: true,
|
||||||
wantDisruptionTargetCond: true,
|
wantDisruptionTargetCond: false,
|
||||||
},
|
|
||||||
"running pod with PodDisruptionConditions disabled": {
|
|
||||||
enablePodDisruptionConditions: false,
|
|
||||||
phase: v1.PodRunning,
|
|
||||||
wantDisruptionTargetCond: false,
|
|
||||||
},
|
|
||||||
"running pod with PodDisruptionConditions enabled should not update conditions in dry-run mode": {
|
|
||||||
enablePodDisruptionConditions: true,
|
|
||||||
phase: v1.PodRunning,
|
|
||||||
dryRun: true,
|
|
||||||
wantDisruptionTargetCond: false,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for name, tc := range cases {
|
for name, tc := range cases {
|
||||||
@ -386,7 +369,6 @@ func TestEvictionWithFinalizers(t *testing.T) {
|
|||||||
|
|
||||||
ns := framework.CreateNamespaceOrDie(clientSet, "eviction-with-finalizers", t)
|
ns := framework.CreateNamespaceOrDie(clientSet, "eviction-with-finalizers", t)
|
||||||
defer framework.DeleteNamespaceOrDie(clientSet, ns, t)
|
defer framework.DeleteNamespaceOrDie(clientSet, ns, t)
|
||||||
featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.PodDisruptionConditions, tc.enablePodDisruptionConditions)
|
|
||||||
defer tCtx.Cancel("test has completed")
|
defer tCtx.Cancel("test has completed")
|
||||||
|
|
||||||
informers.Start(tCtx.Done())
|
informers.Start(tCtx.Done())
|
||||||
|
@ -51,31 +51,21 @@ func TestEvictionForNoExecuteTaintAddedByUser(t *testing.T) {
|
|||||||
nodeIndex := 1 // the exact node doesn't matter, pick one
|
nodeIndex := 1 // the exact node doesn't matter, pick one
|
||||||
|
|
||||||
tests := map[string]struct {
|
tests := map[string]struct {
|
||||||
enablePodDisruptionConditions bool
|
|
||||||
enableSeparateTaintEvictionController bool
|
enableSeparateTaintEvictionController bool
|
||||||
startStandaloneTaintEvictionController bool
|
startStandaloneTaintEvictionController bool
|
||||||
wantPodEvicted bool
|
wantPodEvicted bool
|
||||||
}{
|
}{
|
||||||
"Test eviction for NoExecute taint added by user; pod condition added when PodDisruptionConditions enabled; separate taint eviction controller disabled": {
|
"Test eviction for NoExecute taint added by user; pod condition added; separate taint eviction controller disabled": {
|
||||||
enablePodDisruptionConditions: true,
|
|
||||||
enableSeparateTaintEvictionController: false,
|
|
||||||
startStandaloneTaintEvictionController: false,
|
|
||||||
wantPodEvicted: true,
|
|
||||||
},
|
|
||||||
"Test eviction for NoExecute taint added by user; no pod condition added when PodDisruptionConditions disabled; separate taint eviction controller disabled": {
|
|
||||||
enablePodDisruptionConditions: false,
|
|
||||||
enableSeparateTaintEvictionController: false,
|
enableSeparateTaintEvictionController: false,
|
||||||
startStandaloneTaintEvictionController: false,
|
startStandaloneTaintEvictionController: false,
|
||||||
wantPodEvicted: true,
|
wantPodEvicted: true,
|
||||||
},
|
},
|
||||||
"Test eviction for NoExecute taint added by user; separate taint eviction controller enabled but not started": {
|
"Test eviction for NoExecute taint added by user; separate taint eviction controller enabled but not started": {
|
||||||
enablePodDisruptionConditions: false,
|
|
||||||
enableSeparateTaintEvictionController: true,
|
enableSeparateTaintEvictionController: true,
|
||||||
startStandaloneTaintEvictionController: false,
|
startStandaloneTaintEvictionController: false,
|
||||||
wantPodEvicted: false,
|
wantPodEvicted: false,
|
||||||
},
|
},
|
||||||
"Test eviction for NoExecute taint added by user; separate taint eviction controller enabled and started": {
|
"Test eviction for NoExecute taint added by user; separate taint eviction controller enabled and started": {
|
||||||
enablePodDisruptionConditions: false,
|
|
||||||
enableSeparateTaintEvictionController: true,
|
enableSeparateTaintEvictionController: true,
|
||||||
startStandaloneTaintEvictionController: true,
|
startStandaloneTaintEvictionController: true,
|
||||||
wantPodEvicted: true,
|
wantPodEvicted: true,
|
||||||
@ -124,7 +114,6 @@ func TestEvictionForNoExecuteTaintAddedByUser(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.PodDisruptionConditions, test.enablePodDisruptionConditions)
|
|
||||||
featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.SeparateTaintEvictionController, test.enableSeparateTaintEvictionController)
|
featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.SeparateTaintEvictionController, test.enableSeparateTaintEvictionController)
|
||||||
testCtx := testutils.InitTestAPIServer(t, "taint-no-execute", nil)
|
testCtx := testutils.InitTestAPIServer(t, "taint-no-execute", nil)
|
||||||
cs := testCtx.ClientSet
|
cs := testCtx.ClientSet
|
||||||
@ -202,9 +191,9 @@ func TestEvictionForNoExecuteTaintAddedByUser(t *testing.T) {
|
|||||||
t.Fatalf("Test Failed: error: %q, while getting updated pod", err)
|
t.Fatalf("Test Failed: error: %q, while getting updated pod", err)
|
||||||
}
|
}
|
||||||
_, cond := podutil.GetPodCondition(&testPod.Status, v1.DisruptionTarget)
|
_, cond := podutil.GetPodCondition(&testPod.Status, v1.DisruptionTarget)
|
||||||
if test.enablePodDisruptionConditions && cond == nil {
|
if test.wantPodEvicted && cond == nil {
|
||||||
t.Errorf("Pod %q does not have the expected condition: %q", klog.KObj(testPod), v1.DisruptionTarget)
|
t.Errorf("Pod %q does not have the expected condition: %q", klog.KObj(testPod), v1.DisruptionTarget)
|
||||||
} else if !test.enablePodDisruptionConditions && cond != nil {
|
} else if !test.wantPodEvicted && cond != nil {
|
||||||
t.Errorf("Pod %q has an unexpected condition: %q", klog.KObj(testPod), v1.DisruptionTarget)
|
t.Errorf("Pod %q has an unexpected condition: %q", klog.KObj(testPod), v1.DisruptionTarget)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -40,16 +40,14 @@ import (
|
|||||||
// TestPodGcOrphanedPodsWithFinalizer tests deletion of orphaned pods
|
// TestPodGcOrphanedPodsWithFinalizer tests deletion of orphaned pods
|
||||||
func TestPodGcOrphanedPodsWithFinalizer(t *testing.T) {
|
func TestPodGcOrphanedPodsWithFinalizer(t *testing.T) {
|
||||||
tests := map[string]struct {
|
tests := map[string]struct {
|
||||||
enablePodDisruptionConditions bool
|
|
||||||
enableJobPodReplacementPolicy bool
|
enableJobPodReplacementPolicy bool
|
||||||
phase v1.PodPhase
|
phase v1.PodPhase
|
||||||
wantPhase v1.PodPhase
|
wantPhase v1.PodPhase
|
||||||
wantDisruptionTarget *v1.PodCondition
|
wantDisruptionTarget *v1.PodCondition
|
||||||
}{
|
}{
|
||||||
"PodDisruptionConditions enabled": {
|
"pending pod": {
|
||||||
enablePodDisruptionConditions: true,
|
phase: v1.PodPending,
|
||||||
phase: v1.PodPending,
|
wantPhase: v1.PodFailed,
|
||||||
wantPhase: v1.PodFailed,
|
|
||||||
wantDisruptionTarget: &v1.PodCondition{
|
wantDisruptionTarget: &v1.PodCondition{
|
||||||
Type: v1.DisruptionTarget,
|
Type: v1.DisruptionTarget,
|
||||||
Status: v1.ConditionTrue,
|
Status: v1.ConditionTrue,
|
||||||
@ -57,8 +55,7 @@ func TestPodGcOrphanedPodsWithFinalizer(t *testing.T) {
|
|||||||
Message: "PodGC: node no longer exists",
|
Message: "PodGC: node no longer exists",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"PodDisruptionConditions and PodReplacementPolicy enabled": {
|
"pending pod; PodReplacementPolicy enabled": {
|
||||||
enablePodDisruptionConditions: true,
|
|
||||||
enableJobPodReplacementPolicy: true,
|
enableJobPodReplacementPolicy: true,
|
||||||
phase: v1.PodPending,
|
phase: v1.PodPending,
|
||||||
wantPhase: v1.PodFailed,
|
wantPhase: v1.PodFailed,
|
||||||
@ -69,32 +66,18 @@ func TestPodGcOrphanedPodsWithFinalizer(t *testing.T) {
|
|||||||
Message: "PodGC: node no longer exists",
|
Message: "PodGC: node no longer exists",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Only PodReplacementPolicy enabled; no PodDisruptionCondition": {
|
"succeeded pod": {
|
||||||
enablePodDisruptionConditions: false,
|
phase: v1.PodSucceeded,
|
||||||
enableJobPodReplacementPolicy: true,
|
wantPhase: v1.PodSucceeded,
|
||||||
phase: v1.PodPending,
|
|
||||||
wantPhase: v1.PodFailed,
|
|
||||||
},
|
},
|
||||||
"PodDisruptionConditions disabled": {
|
"failed pod": {
|
||||||
enablePodDisruptionConditions: false,
|
phase: v1.PodFailed,
|
||||||
phase: v1.PodPending,
|
wantPhase: v1.PodFailed,
|
||||||
wantPhase: v1.PodPending,
|
|
||||||
},
|
|
||||||
"PodDisruptionConditions enabled; succeeded pod": {
|
|
||||||
enablePodDisruptionConditions: true,
|
|
||||||
phase: v1.PodSucceeded,
|
|
||||||
wantPhase: v1.PodSucceeded,
|
|
||||||
},
|
|
||||||
"PodDisruptionConditions enabled; failed pod": {
|
|
||||||
enablePodDisruptionConditions: true,
|
|
||||||
phase: v1.PodFailed,
|
|
||||||
wantPhase: v1.PodFailed,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, test := range tests {
|
for name, test := range tests {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodDisruptionConditions, test.enablePodDisruptionConditions)
|
|
||||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.JobPodReplacementPolicy, test.enableJobPodReplacementPolicy)
|
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.JobPodReplacementPolicy, test.enableJobPodReplacementPolicy)
|
||||||
testCtx := setup(t, "podgc-orphaned")
|
testCtx := setup(t, "podgc-orphaned")
|
||||||
cs := testCtx.ClientSet
|
cs := testCtx.ClientSet
|
||||||
@ -170,31 +153,18 @@ func TestPodGcOrphanedPodsWithFinalizer(t *testing.T) {
|
|||||||
// TestTerminatingOnOutOfServiceNode tests deletion pods terminating on out-of-service nodes
|
// TestTerminatingOnOutOfServiceNode tests deletion pods terminating on out-of-service nodes
|
||||||
func TestTerminatingOnOutOfServiceNode(t *testing.T) {
|
func TestTerminatingOnOutOfServiceNode(t *testing.T) {
|
||||||
tests := map[string]struct {
|
tests := map[string]struct {
|
||||||
enablePodDisruptionConditions bool
|
|
||||||
enableJobPodReplacementPolicy bool
|
enableJobPodReplacementPolicy bool
|
||||||
withFinalizer bool
|
withFinalizer bool
|
||||||
wantPhase v1.PodPhase
|
wantPhase v1.PodPhase
|
||||||
}{
|
}{
|
||||||
"pod has phase changed to Failed when PodDisruptionConditions enabled": {
|
"pod has phase changed to Failed": {
|
||||||
enablePodDisruptionConditions: true,
|
withFinalizer: true,
|
||||||
withFinalizer: true,
|
wantPhase: v1.PodFailed,
|
||||||
wantPhase: v1.PodFailed,
|
|
||||||
},
|
},
|
||||||
"pod has phase unchanged when PodDisruptionConditions disabled": {
|
"pod is getting deleted when no finalizer": {
|
||||||
enablePodDisruptionConditions: false,
|
withFinalizer: false,
|
||||||
withFinalizer: true,
|
|
||||||
wantPhase: v1.PodPending,
|
|
||||||
},
|
},
|
||||||
"pod is getting deleted when no finalizer and PodDisruptionConditions enabled": {
|
"pod has phase changed when JobPodReplacementPolicy enabled": {
|
||||||
enablePodDisruptionConditions: true,
|
|
||||||
withFinalizer: false,
|
|
||||||
},
|
|
||||||
"pod is getting deleted when no finalizer and PodDisruptionConditions disabled": {
|
|
||||||
enablePodDisruptionConditions: false,
|
|
||||||
withFinalizer: false,
|
|
||||||
},
|
|
||||||
"pod has phase changed when PodDisruptionConditions disabled, but JobPodReplacementPolicy enabled": {
|
|
||||||
enablePodDisruptionConditions: false,
|
|
||||||
enableJobPodReplacementPolicy: true,
|
enableJobPodReplacementPolicy: true,
|
||||||
withFinalizer: true,
|
withFinalizer: true,
|
||||||
wantPhase: v1.PodFailed,
|
wantPhase: v1.PodFailed,
|
||||||
@ -203,7 +173,6 @@ func TestTerminatingOnOutOfServiceNode(t *testing.T) {
|
|||||||
|
|
||||||
for name, test := range tests {
|
for name, test := range tests {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodDisruptionConditions, test.enablePodDisruptionConditions)
|
|
||||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.NodeOutOfServiceVolumeDetach, true)
|
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.NodeOutOfServiceVolumeDetach, true)
|
||||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.JobPodReplacementPolicy, test.enableJobPodReplacementPolicy)
|
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.JobPodReplacementPolicy, test.enableJobPodReplacementPolicy)
|
||||||
testCtx := setup(t, "podgc-out-of-service")
|
testCtx := setup(t, "podgc-out-of-service")
|
||||||
@ -385,7 +354,6 @@ func TestPodGcForPodsWithDuplicatedFieldKeys(t *testing.T) {
|
|||||||
|
|
||||||
for name, test := range tests {
|
for name, test := range tests {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodDisruptionConditions, true)
|
|
||||||
testCtx := setup(t, "podgc-orphaned")
|
testCtx := setup(t, "podgc-orphaned")
|
||||||
cs := testCtx.ClientSet
|
cs := testCtx.ClientSet
|
||||||
|
|
||||||
|
@ -33,17 +33,14 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/apimachinery/pkg/util/intstr"
|
"k8s.io/apimachinery/pkg/util/intstr"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
"k8s.io/apiserver/pkg/util/feature"
|
|
||||||
"k8s.io/client-go/informers"
|
"k8s.io/client-go/informers"
|
||||||
clientset "k8s.io/client-go/kubernetes"
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
restclient "k8s.io/client-go/rest"
|
restclient "k8s.io/client-go/rest"
|
||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
|
||||||
"k8s.io/component-helpers/storage/volume"
|
"k8s.io/component-helpers/storage/volume"
|
||||||
"k8s.io/klog/v2"
|
"k8s.io/klog/v2"
|
||||||
configv1 "k8s.io/kube-scheduler/config/v1"
|
configv1 "k8s.io/kube-scheduler/config/v1"
|
||||||
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
|
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
|
||||||
"k8s.io/kubernetes/pkg/apis/scheduling"
|
"k8s.io/kubernetes/pkg/apis/scheduling"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
"k8s.io/kubernetes/pkg/scheduler"
|
"k8s.io/kubernetes/pkg/scheduler"
|
||||||
configtesting "k8s.io/kubernetes/pkg/scheduler/apis/config/testing"
|
configtesting "k8s.io/kubernetes/pkg/scheduler/apis/config/testing"
|
||||||
"k8s.io/kubernetes/pkg/scheduler/framework"
|
"k8s.io/kubernetes/pkg/scheduler/framework"
|
||||||
@ -200,41 +197,14 @@ func TestPreemption(t *testing.T) {
|
|||||||
|
|
||||||
maxTokens := 1000
|
maxTokens := 1000
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
existingPods []*v1.Pod
|
existingPods []*v1.Pod
|
||||||
pod *v1.Pod
|
pod *v1.Pod
|
||||||
initTokens int
|
initTokens int
|
||||||
enablePreFilter bool
|
enablePreFilter bool
|
||||||
unresolvable bool
|
unresolvable bool
|
||||||
preemptedPodIndexes map[int]struct{}
|
preemptedPodIndexes map[int]struct{}
|
||||||
enablePodDisruptionConditions bool
|
|
||||||
}{
|
}{
|
||||||
{
|
|
||||||
name: "basic pod preemption with PodDisruptionConditions enabled",
|
|
||||||
initTokens: maxTokens,
|
|
||||||
existingPods: []*v1.Pod{
|
|
||||||
initPausePod(&testutils.PausePodConfig{
|
|
||||||
Name: "victim-pod",
|
|
||||||
Namespace: testCtx.NS.Name,
|
|
||||||
Priority: &lowPriority,
|
|
||||||
Resources: &v1.ResourceRequirements{Requests: v1.ResourceList{
|
|
||||||
v1.ResourceCPU: *resource.NewMilliQuantity(400, resource.DecimalSI),
|
|
||||||
v1.ResourceMemory: *resource.NewQuantity(200, resource.DecimalSI)},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
pod: initPausePod(&testutils.PausePodConfig{
|
|
||||||
Name: "preemptor-pod",
|
|
||||||
Namespace: testCtx.NS.Name,
|
|
||||||
Priority: &highPriority,
|
|
||||||
Resources: &v1.ResourceRequirements{Requests: v1.ResourceList{
|
|
||||||
v1.ResourceCPU: *resource.NewMilliQuantity(300, resource.DecimalSI),
|
|
||||||
v1.ResourceMemory: *resource.NewQuantity(200, resource.DecimalSI)},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
preemptedPodIndexes: map[int]struct{}{0: {}},
|
|
||||||
enablePodDisruptionConditions: true,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "basic pod preemption",
|
name: "basic pod preemption",
|
||||||
initTokens: maxTokens,
|
initTokens: maxTokens,
|
||||||
@ -484,7 +454,6 @@ func TestPreemption(t *testing.T) {
|
|||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.PodDisruptionConditions, test.enablePodDisruptionConditions)
|
|
||||||
filter.Tokens = test.initTokens
|
filter.Tokens = test.initTokens
|
||||||
filter.EnablePreFilter = test.enablePreFilter
|
filter.EnablePreFilter = test.enablePreFilter
|
||||||
filter.Unresolvable = test.unresolvable
|
filter.Unresolvable = test.unresolvable
|
||||||
@ -513,10 +482,8 @@ func TestPreemption(t *testing.T) {
|
|||||||
t.Errorf("Error %v when getting the updated status for pod %v/%v ", err, p.Namespace, p.Name)
|
t.Errorf("Error %v when getting the updated status for pod %v/%v ", err, p.Namespace, p.Name)
|
||||||
}
|
}
|
||||||
_, cond := podutil.GetPodCondition(&pod.Status, v1.DisruptionTarget)
|
_, cond := podutil.GetPodCondition(&pod.Status, v1.DisruptionTarget)
|
||||||
if test.enablePodDisruptionConditions && cond == nil {
|
if cond == nil {
|
||||||
t.Errorf("Pod %q does not have the expected condition: %q", klog.KObj(pod), v1.DisruptionTarget)
|
t.Errorf("Pod %q does not have the expected condition: %q", klog.KObj(pod), v1.DisruptionTarget)
|
||||||
} else if test.enablePodDisruptionConditions == false && cond != nil {
|
|
||||||
t.Errorf("Pod %q has an unexpected condition: %q", klog.KObj(pod), v1.DisruptionTarget)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if p.DeletionTimestamp != nil {
|
if p.DeletionTimestamp != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user