@@ -19,16 +19,23 @@ package drain
19
19
import (
20
20
"errors"
21
21
"fmt"
22
+ "os"
23
+ "reflect"
24
+ "sort"
22
25
"strconv"
23
26
"testing"
24
27
"time"
25
28
26
29
corev1 "k8s.io/api/core/v1"
30
+ policyv1beta1 "k8s.io/api/policy/v1beta1"
27
31
apierrors "k8s.io/apimachinery/pkg/api/errors"
28
32
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
33
+ "k8s.io/apimachinery/pkg/runtime"
29
34
"k8s.io/apimachinery/pkg/runtime/schema"
30
35
"k8s.io/apimachinery/pkg/types"
31
36
"k8s.io/apimachinery/pkg/util/wait"
37
+ "k8s.io/client-go/kubernetes/fake"
38
+ ktest "k8s.io/client-go/testing"
32
39
)
33
40
34
41
func TestDeletePods (t * testing.T ) {
@@ -145,3 +152,155 @@ func createPods(ifCreateNewPods bool) (map[string]corev1.Pod, []corev1.Pod) {
145
152
}
146
153
return podMap , podSlice
147
154
}
155
+
156
+ // addEvictionSupport implements simple fake eviction support on the fake.Clientset
157
+ func addEvictionSupport (t * testing.T , k * fake.Clientset ) {
158
+ podsEviction := metav1.APIResource {
159
+ Name : "pods/eviction" ,
160
+ Kind : "Eviction" ,
161
+ Group : "" ,
162
+ Version : "v1" ,
163
+ }
164
+ coreResources := & metav1.APIResourceList {
165
+ GroupVersion : "v1" ,
166
+ APIResources : []metav1.APIResource {podsEviction },
167
+ }
168
+
169
+ policyResources := & metav1.APIResourceList {
170
+ GroupVersion : "policy/v1" ,
171
+ }
172
+ k .Resources = append (k .Resources , coreResources , policyResources )
173
+
174
+ // Delete pods when evict is called
175
+ k .PrependReactor ("create" , "pods" , func (action ktest.Action ) (bool , runtime.Object , error ) {
176
+ if action .GetSubresource () != "eviction" {
177
+ return false , nil , nil
178
+ }
179
+
180
+ eviction := * action .(ktest.CreateAction ).GetObject ().(* policyv1beta1.Eviction )
181
+ // Avoid the lock
182
+ go func () {
183
+ err := k .CoreV1 ().Pods (eviction .Namespace ).Delete (eviction .Name , & metav1.DeleteOptions {})
184
+ if err != nil {
185
+ // Errorf because we can't call Fatalf from another goroutine
186
+ t .Errorf ("failed to delete pod: %s/%s" , eviction .Namespace , eviction .Name )
187
+ }
188
+ }()
189
+
190
+ return true , nil , nil
191
+ })
192
+ }
193
+
194
+ func TestCheckEvictionSupport (t * testing.T ) {
195
+ for _ , evictionSupported := range []bool {true , false } {
196
+ evictionSupported := evictionSupported
197
+ t .Run (fmt .Sprintf ("evictionSupported=%v" , evictionSupported ),
198
+ func (t * testing.T ) {
199
+ k := fake .NewSimpleClientset ()
200
+ if evictionSupported {
201
+ addEvictionSupport (t , k )
202
+ }
203
+
204
+ apiGroup , err := CheckEvictionSupport (k )
205
+ if err != nil {
206
+ t .Fatalf ("unexpected error: %v" , err )
207
+ }
208
+ expectedAPIGroup := ""
209
+ if evictionSupported {
210
+ expectedAPIGroup = "policy/v1"
211
+ }
212
+ if apiGroup != expectedAPIGroup {
213
+ t .Fatalf ("expected apigroup %q, actual=%q" , expectedAPIGroup , apiGroup )
214
+ }
215
+ })
216
+ }
217
+ }
218
+
219
+ func TestDeleteOrEvict (t * testing.T ) {
220
+ for _ , evictionSupported := range []bool {true , false } {
221
+ evictionSupported := evictionSupported
222
+ t .Run (fmt .Sprintf ("evictionSupported=%v" , evictionSupported ),
223
+ func (t * testing.T ) {
224
+ h := & Helper {
225
+ Out : os .Stdout ,
226
+ GracePeriodSeconds : 10 ,
227
+ }
228
+
229
+ // Create 4 pods, and try to remove the first 2
230
+ var expectedEvictions []policyv1beta1.Eviction
231
+ var create []runtime.Object
232
+ deletePods := []corev1.Pod {}
233
+ for i := 1 ; i <= 4 ; i ++ {
234
+ pod := & corev1.Pod {}
235
+ pod .Name = fmt .Sprintf ("mypod-%d" , i )
236
+ pod .Namespace = "default"
237
+
238
+ create = append (create , pod )
239
+ if i <= 2 {
240
+ deletePods = append (deletePods , * pod )
241
+
242
+ if evictionSupported {
243
+ eviction := policyv1beta1.Eviction {}
244
+ eviction .Kind = "Eviction"
245
+ eviction .APIVersion = "policy/v1"
246
+ eviction .Namespace = pod .Namespace
247
+ eviction .Name = pod .Name
248
+
249
+ gracePeriodSeconds := int64 (h .GracePeriodSeconds )
250
+ eviction .DeleteOptions = & metav1.DeleteOptions {
251
+ GracePeriodSeconds : & gracePeriodSeconds ,
252
+ }
253
+
254
+ expectedEvictions = append (expectedEvictions , eviction )
255
+ }
256
+ }
257
+ }
258
+
259
+ // Build the fake client
260
+ k := fake .NewSimpleClientset (create ... )
261
+ if evictionSupported {
262
+ addEvictionSupport (t , k )
263
+ }
264
+ h .Client = k
265
+
266
+ // Do the eviction
267
+ if err := h .DeleteOrEvictPods (deletePods ); err != nil {
268
+ t .Fatalf ("error from DeleteOrEvictPods: %v" , err )
269
+ }
270
+
271
+ // Test that other pods are still there
272
+ var remainingPods []string
273
+ {
274
+ podList , err := k .CoreV1 ().Pods ("" ).List (metav1.ListOptions {})
275
+ if err != nil {
276
+ t .Fatalf ("error listing pods: %v" , err )
277
+ }
278
+
279
+ for _ , pod := range podList .Items {
280
+ remainingPods = append (remainingPods , pod .Namespace + "/" + pod .Name )
281
+ }
282
+ sort .Strings (remainingPods )
283
+ }
284
+ expected := []string {"default/mypod-3" , "default/mypod-4" }
285
+ if ! reflect .DeepEqual (remainingPods , expected ) {
286
+ t .Errorf ("unexpected remaining pods after DeleteOrEvictPods; actual %v; expected %v" , remainingPods , expected )
287
+ }
288
+
289
+ // Test that pods were evicted as expected
290
+ var actualEvictions []policyv1beta1.Eviction
291
+ for _ , action := range k .Actions () {
292
+ if action .GetVerb () != "create" || action .GetResource ().Resource != "pods" || action .GetSubresource () != "eviction" {
293
+ continue
294
+ }
295
+ eviction := * action .(ktest.CreateAction ).GetObject ().(* policyv1beta1.Eviction )
296
+ actualEvictions = append (actualEvictions , eviction )
297
+ }
298
+ sort .Slice (actualEvictions , func (i , j int ) bool {
299
+ return actualEvictions [i ].Name < actualEvictions [j ].Name
300
+ })
301
+ if ! reflect .DeepEqual (actualEvictions , expectedEvictions ) {
302
+ t .Errorf ("unexpected evictions; actual %v; expected %v" , actualEvictions , expectedEvictions )
303
+ }
304
+ })
305
+ }
306
+ }
0 commit comments