@@ -34,6 +34,7 @@ import (
34
34
"k8s.io/apimachinery/pkg/runtime"
35
35
"k8s.io/apimachinery/pkg/watch"
36
36
clientset "k8s.io/client-go/kubernetes"
37
+ v1qos "k8s.io/kubernetes/pkg/apis/core/v1/helper/qos"
37
38
"k8s.io/kubernetes/pkg/apis/scheduling"
38
39
"k8s.io/kubernetes/test/e2e/framework"
39
40
e2enode "k8s.io/kubernetes/test/e2e/framework/node"
@@ -103,57 +104,77 @@ var _ = SIGDescribe("SchedulerPreemption [Serial]", func() {
103
104
var podRes v1.ResourceList
104
105
// Create one pod per node that uses a lot of the node's resources.
105
106
ginkgo .By ("Create pods that use 60% of node resources." )
106
- pods := make ([]* v1.Pod , len (nodeList .Items ))
107
+ pods := make ([]* v1.Pod , 0 , len (nodeList .Items ))
108
+ allPods , err := cs .CoreV1 ().Pods (metav1 .NamespaceAll ).List (metav1.ListOptions {})
109
+ framework .ExpectNoError (err )
107
110
for i , node := range nodeList .Items {
111
+ currentCPUUsage , currentMemUsage := getCurrentPodUsageOnTheNode (node .Name , allPods .Items , podRequestedResource )
112
+ framework .Logf ("Current cpu and memory usage %v, %v" , currentCPUUsage , currentMemUsage )
108
113
cpuAllocatable , found := node .Status .Allocatable ["cpu" ]
109
114
framework .ExpectEqual (found , true )
110
- milliCPU := cpuAllocatable .MilliValue () * 40 / 100
115
+ milliCPU := cpuAllocatable .MilliValue ()
116
+ milliCPU = int64 (float64 (milliCPU - currentCPUUsage ) * float64 (0.6 ))
111
117
memAllocatable , found := node .Status .Allocatable ["memory" ]
112
118
framework .ExpectEqual (found , true )
113
- memory := memAllocatable .Value () * 60 / 100
119
+ memory := memAllocatable .Value ()
120
+ memory = int64 (float64 (memory - currentMemUsage ) * float64 (0.6 ))
121
+ // If a node is already heavily utilized let not's create a pod there.
122
+ if milliCPU <= 0 || memory <= 0 {
123
+ framework .Logf ("Node is heavily utilized, let's not create a pod here" )
124
+ continue
125
+ }
114
126
podRes = v1.ResourceList {}
115
127
podRes [v1 .ResourceCPU ] = * resource .NewMilliQuantity (int64 (milliCPU ), resource .DecimalSI )
116
128
podRes [v1 .ResourceMemory ] = * resource .NewQuantity (int64 (memory ), resource .BinarySI )
117
129
118
130
// make the first pod low priority and the rest medium priority.
119
131
priorityName := mediumPriorityClassName
120
- if i == 0 {
132
+ if len ( pods ) == 0 {
121
133
priorityName = lowPriorityClassName
122
134
}
123
- pods [ i ] = createPausePod (f , pausePodConfig {
135
+ pods = append ( pods , createPausePod (f , pausePodConfig {
124
136
Name : fmt .Sprintf ("pod%d-%v" , i , priorityName ),
125
137
PriorityClassName : priorityName ,
126
138
Resources : & v1.ResourceRequirements {
127
139
Requests : podRes ,
128
140
},
129
- })
141
+ NodeName : node .Name ,
142
+ }))
130
143
framework .Logf ("Created pod: %v" , pods [i ].Name )
131
144
}
145
+ if len (pods ) < 2 {
146
+ framework .Failf ("We need at least two pods to be created but" +
147
+ "all nodes are already heavily utilized, so preemption tests cannot be run" )
148
+ }
132
149
ginkgo .By ("Wait for pods to be scheduled." )
133
150
for _ , pod := range pods {
134
151
framework .ExpectNoError (e2epod .WaitForPodRunningInNamespace (cs , pod ))
135
152
}
136
153
137
- ginkgo .By ("Run a high priority pod that use 60% of a node resources." )
138
- // Create a high priority pod and make sure it is scheduled.
154
+ // Set the pod request to the first pod's resources (should be low priority pod)
155
+ podRes = pods [0 ].Spec .Containers [0 ].Resources .Requests
156
+
157
+ ginkgo .By ("Run a high priority pod that has same requirements as that of lower priority pod" )
158
+ // Create a high priority pod and make sure it is scheduled on the same node as the low priority pod.
139
159
runPausePod (f , pausePodConfig {
140
160
Name : "preemptor-pod" ,
141
161
PriorityClassName : highPriorityClassName ,
142
162
Resources : & v1.ResourceRequirements {
143
163
Requests : podRes ,
144
164
},
165
+ NodeName : pods [0 ].Spec .NodeName ,
145
166
})
146
- // Make sure that the lowest priority pod is deleted.
167
+
147
168
preemptedPod , err := cs .CoreV1 ().Pods (pods [0 ].Namespace ).Get (pods [0 ].Name , metav1.GetOptions {})
148
- podDeleted := (err != nil && apierrors .IsNotFound (err )) ||
169
+ podPreempted := (err != nil && apierrors .IsNotFound (err )) ||
149
170
(err == nil && preemptedPod .DeletionTimestamp != nil )
150
- framework .ExpectEqual (podDeleted , true )
151
- // Other pods (mid priority ones) should be present.
152
171
for i := 1 ; i < len (pods ); i ++ {
153
172
livePod , err := cs .CoreV1 ().Pods (pods [i ].Namespace ).Get (pods [i ].Name , metav1.GetOptions {})
154
173
framework .ExpectNoError (err )
155
174
gomega .Expect (livePod .DeletionTimestamp ).To (gomega .BeNil ())
156
175
}
176
+
177
+ framework .ExpectEqual (podPreempted , true )
157
178
})
158
179
159
180
// This test verifies that when a critical pod is created and no node with
@@ -163,21 +184,32 @@ var _ = SIGDescribe("SchedulerPreemption [Serial]", func() {
163
184
var podRes v1.ResourceList
164
185
// Create one pod per node that uses a lot of the node's resources.
165
186
ginkgo .By ("Create pods that use 60% of node resources." )
166
- pods := make ([]* v1.Pod , len (nodeList .Items ))
187
+ pods := make ([]* v1.Pod , 0 , len (nodeList .Items ))
188
+ allPods , err := cs .CoreV1 ().Pods (metav1 .NamespaceAll ).List (metav1.ListOptions {})
189
+ framework .ExpectNoError (err )
167
190
for i , node := range nodeList .Items {
191
+ currentCPUUsage , currentMemUsage := getCurrentPodUsageOnTheNode (node .Name , allPods .Items , podRequestedResource )
192
+ framework .Logf ("Current cpu usage and memory usage is %v, %v" , currentCPUUsage , currentMemUsage )
168
193
cpuAllocatable , found := node .Status .Allocatable ["cpu" ]
169
194
framework .ExpectEqual (found , true )
170
- milliCPU := cpuAllocatable .MilliValue () * 40 / 100
195
+ milliCPU := cpuAllocatable .MilliValue ()
196
+ milliCPU = int64 (float64 (milliCPU - currentCPUUsage ) * float64 (0.6 ))
171
197
memAllocatable , found := node .Status .Allocatable ["memory" ]
172
198
framework .ExpectEqual (found , true )
173
- memory := memAllocatable .Value () * 60 / 100
199
+ memory := memAllocatable .Value ()
200
+ memory = int64 (float64 (memory - currentMemUsage ) * float64 (0.6 ))
174
201
podRes = v1.ResourceList {}
202
+ // If a node is already heavily utilized let not's create a pod there.
203
+ if milliCPU <= 0 || memory <= 0 {
204
+ framework .Logf ("Node is heavily utilized, let's not create a pod there" )
205
+ continue
206
+ }
175
207
podRes [v1 .ResourceCPU ] = * resource .NewMilliQuantity (int64 (milliCPU ), resource .DecimalSI )
176
208
podRes [v1 .ResourceMemory ] = * resource .NewQuantity (int64 (memory ), resource .BinarySI )
177
209
178
210
// make the first pod low priority and the rest medium priority.
179
211
priorityName := mediumPriorityClassName
180
- if i == 0 {
212
+ if len ( pods ) == 0 {
181
213
priorityName = lowPriorityClassName
182
214
}
183
215
pods [i ] = createPausePod (f , pausePodConfig {
@@ -186,15 +218,22 @@ var _ = SIGDescribe("SchedulerPreemption [Serial]", func() {
186
218
Resources : & v1.ResourceRequirements {
187
219
Requests : podRes ,
188
220
},
221
+ NodeName : node .Name ,
189
222
})
190
223
framework .Logf ("Created pod: %v" , pods [i ].Name )
191
224
}
225
+ if len (pods ) < 2 {
226
+ framework .Skipf ("We need at least two pods to be created but" +
227
+ "all nodes are already heavily utilized, so preemption tests cannot be run" )
228
+ }
192
229
ginkgo .By ("Wait for pods to be scheduled." )
193
230
for _ , pod := range pods {
194
231
framework .ExpectNoError (e2epod .WaitForPodRunningInNamespace (cs , pod ))
195
232
}
196
233
197
- ginkgo .By ("Run a critical pod that use 60% of a node resources." )
234
+ // We want this pod to be preempted
235
+ podRes = pods [0 ].Spec .Containers [0 ].Resources .Requests
236
+ ginkgo .By ("Run a critical pod that use same resources as that of a lower priority pod" )
198
237
// Create a critical pod and make sure it is scheduled.
199
238
defer func () {
200
239
// Clean-up the critical pod
@@ -211,18 +250,25 @@ var _ = SIGDescribe("SchedulerPreemption [Serial]", func() {
211
250
Resources : & v1.ResourceRequirements {
212
251
Requests : podRes ,
213
252
},
253
+ NodeName : pods [0 ].Spec .NodeName ,
214
254
})
255
+
256
+ defer func () {
257
+ // Clean-up the critical pod
258
+ err := f .ClientSet .CoreV1 ().Pods (metav1 .NamespaceSystem ).Delete ("critical-pod" , metav1 .NewDeleteOptions (0 ))
259
+ framework .ExpectNoError (err )
260
+ }()
215
261
// Make sure that the lowest priority pod is deleted.
216
262
preemptedPod , err := cs .CoreV1 ().Pods (pods [0 ].Namespace ).Get (pods [0 ].Name , metav1.GetOptions {})
217
- podDeleted := (err != nil && apierrors .IsNotFound (err )) ||
263
+ podPreempted := (err != nil && apierrors .IsNotFound (err )) ||
218
264
(err == nil && preemptedPod .DeletionTimestamp != nil )
219
- framework .ExpectEqual (podDeleted , true )
220
- // Other pods (mid priority ones) should be present.
221
265
for i := 1 ; i < len (pods ); i ++ {
222
266
livePod , err := cs .CoreV1 ().Pods (pods [i ].Namespace ).Get (pods [i ].Name , metav1.GetOptions {})
223
267
framework .ExpectNoError (err )
224
268
gomega .Expect (livePod .DeletionTimestamp ).To (gomega .BeNil ())
225
269
}
270
+
271
+ framework .ExpectEqual (podPreempted , true )
226
272
})
227
273
})
228
274
@@ -516,3 +562,17 @@ func waitForPreemptingWithTimeout(f *framework.Framework, pod *v1.Pod, timeout t
516
562
})
517
563
framework .ExpectNoError (err , "pod %v/%v failed to preempt other pods" , pod .Namespace , pod .Name )
518
564
}
565
+
566
+ func getCurrentPodUsageOnTheNode (nodeName string , pods []v1.Pod , resource * v1.ResourceRequirements ) (int64 , int64 ) {
567
+ totalRequestedCPUResource := resource .Requests .Cpu ().MilliValue ()
568
+ totalRequestedMemResource := resource .Requests .Memory ().Value ()
569
+ for _ , pod := range pods {
570
+ if pod .Spec .NodeName != nodeName || v1qos .GetPodQOS (& pod ) == v1 .PodQOSBestEffort {
571
+ continue
572
+ }
573
+ result := getNonZeroRequests (& pod )
574
+ totalRequestedCPUResource += result .MilliCPU
575
+ totalRequestedMemResource += result .Memory
576
+ }
577
+ return totalRequestedCPUResource , totalRequestedMemResource
578
+ }
0 commit comments