@@ -38,16 +38,19 @@ import (
38
38
"k8s.io/apimachinery/pkg/types"
39
39
"k8s.io/apimachinery/pkg/util/intstr"
40
40
"k8s.io/apimachinery/pkg/util/json"
41
+ "k8s.io/apimachinery/pkg/util/sets"
41
42
"k8s.io/apimachinery/pkg/util/wait"
42
43
"k8s.io/client-go/dynamic"
43
44
"k8s.io/client-go/kubernetes"
44
45
clientscheme "k8s.io/client-go/kubernetes/scheme"
45
46
"k8s.io/client-go/util/retry"
46
47
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
47
48
"k8s.io/kubernetes/test/e2e/framework"
49
+ e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
48
50
e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
49
51
imageutils "k8s.io/kubernetes/test/utils/image"
50
52
admissionapi "k8s.io/pod-security-admission/api"
53
+ "k8s.io/utils/ptr"
51
54
)
52
55
53
56
// schedulingTimeout is longer specifically because sometimes we need to wait
@@ -301,7 +304,7 @@ var _ = SIGDescribe("DisruptionController", func() {
301
304
}
302
305
303
306
if c .maxUnavailable .String () != "" {
304
- createPDBMaxUnavailableOrDie (ctx , cs , ns , defaultName , c .maxUnavailable )
307
+ createPDBMaxUnavailableOrDie (ctx , cs , ns , defaultName , c .maxUnavailable , nil )
305
308
}
306
309
307
310
// Locate a running pod.
@@ -375,7 +378,7 @@ var _ = SIGDescribe("DisruptionController", func() {
375
378
376
379
ginkgo .By ("Trying to evict the same pod we tried earlier which should now be evictable" )
377
380
waitForPodsOrDie (ctx , cs , ns , 3 )
378
- waitForPdbToObserveHealthyPods (ctx , cs , ns , 3 )
381
+ waitForPdbToObserveHealthyPods (ctx , cs , ns , 3 , 2 )
379
382
err = cs .CoreV1 ().Pods (ns ).EvictV1 (ctx , e )
380
383
framework .ExpectNoError (err ) // the eviction is now allowed
381
384
@@ -414,6 +417,112 @@ var _ = SIGDescribe("DisruptionController", func() {
414
417
framework .ExpectNoError (err ) // the eviction is now allowed
415
418
})
416
419
420
+ unhealthyPodEvictionPolicyCases := []struct {
421
+ name string
422
+ unhealthyPodEvictionPolicy * policyv1.UnhealthyPodEvictionPolicyType
423
+ podsShouldBecomeReadyFirst bool
424
+ expectedSuccesfulPodEvictions int
425
+ }{
426
+ {
427
+ name : "should evict ready pods with Default UnhealthyPodEvictionPolicy" ,
428
+ unhealthyPodEvictionPolicy : nil ,
429
+ podsShouldBecomeReadyFirst : true ,
430
+ expectedSuccesfulPodEvictions : 1 ,
431
+ },
432
+ {
433
+ name : "should evict ready pods with IfHealthyBudget UnhealthyPodEvictionPolicy" ,
434
+ unhealthyPodEvictionPolicy : ptr .To (policyv1 .IfHealthyBudget ),
435
+ podsShouldBecomeReadyFirst : true ,
436
+ expectedSuccesfulPodEvictions : 1 ,
437
+ },
438
+ {
439
+ name : "should evict ready pods with AlwaysAllow UnhealthyPodEvictionPolicy" ,
440
+ unhealthyPodEvictionPolicy : ptr .To (policyv1 .AlwaysAllow ),
441
+ podsShouldBecomeReadyFirst : true ,
442
+ expectedSuccesfulPodEvictions : 1 ,
443
+ },
444
+ {
445
+ name : "should not evict unready pods with Default UnhealthyPodEvictionPolicy" ,
446
+ unhealthyPodEvictionPolicy : nil ,
447
+ podsShouldBecomeReadyFirst : false ,
448
+ expectedSuccesfulPodEvictions : 0 ,
449
+ },
450
+ {
451
+ name : "should not evict unready pods with IfHealthyBudget UnhealthyPodEvictionPolicy" ,
452
+ unhealthyPodEvictionPolicy : ptr .To (policyv1 .IfHealthyBudget ),
453
+ podsShouldBecomeReadyFirst : false ,
454
+ expectedSuccesfulPodEvictions : 0 ,
455
+ },
456
+ {
457
+ name : "should evict unready pods with AlwaysAllow UnhealthyPodEvictionPolicy" ,
458
+ unhealthyPodEvictionPolicy : ptr .To (policyv1 .AlwaysAllow ),
459
+ podsShouldBecomeReadyFirst : false ,
460
+ expectedSuccesfulPodEvictions : 3 ,
461
+ },
462
+ }
463
+ for i := range unhealthyPodEvictionPolicyCases {
464
+ tc := unhealthyPodEvictionPolicyCases [i ]
465
+
466
+ framework .It (tc .name , func (ctx context.Context ) {
467
+ ginkgo .By ("Creating a pdb" )
468
+ createPDBMaxUnavailableOrDie (ctx , cs , ns , defaultName , intstr .FromInt32 (1 ), tc .unhealthyPodEvictionPolicy )
469
+
470
+ ginkgo .By ("Creating a replica set" )
471
+ rsName := "test-rs-with-delayed-ready"
472
+ replicas := int32 (3 )
473
+ rs := newRS (rsName , replicas , defaultLabels , WebserverImageName , WebserverImage , nil )
474
+ rs .Labels ["name" ] = rsName
475
+ initialDelaySeconds := framework .PodStartTimeout .Seconds () + 30
476
+ if tc .podsShouldBecomeReadyFirst {
477
+ initialDelaySeconds = 0
478
+ }
479
+ rs .Spec .Template .Spec .Containers [0 ].ReadinessProbe = & v1.Probe {
480
+ ProbeHandler : v1.ProbeHandler {
481
+ HTTPGet : & v1.HTTPGetAction {
482
+ Path : "/index.html" ,
483
+ Port : intstr.IntOrString {IntVal : 80 },
484
+ },
485
+ },
486
+ InitialDelaySeconds : int32 (initialDelaySeconds ),
487
+ }
488
+ _ , err := cs .AppsV1 ().ReplicaSets (ns ).Create (ctx , rs , metav1.CreateOptions {})
489
+ framework .ExpectNoError (err , "Creating replica set %q in namespace %q" , rs .Name , ns )
490
+
491
+ if tc .podsShouldBecomeReadyFirst {
492
+ ginkgo .By ("Wait for pods to be running and ready" )
493
+ waitForPodsOrDie (ctx , cs , ns , int (replicas ))
494
+ waitForPdbToObserveHealthyPods (ctx , cs , ns , replicas , replicas - 1 )
495
+ } else {
496
+ ginkgo .By ("Wait for pods to be running and not ready" )
497
+ err := e2epod .VerifyPodsRunning (ctx , cs , ns , rsName , false , replicas )
498
+ framework .ExpectNoError (err )
499
+ waitForPdbToObserveHealthyPods (ctx , cs , ns , 0 , replicas - 1 )
500
+ }
501
+
502
+ ginkgo .By ("Try to evict all pods guarded by a PDB" )
503
+ podList , err := cs .CoreV1 ().Pods (ns ).List (ctx , metav1.ListOptions {})
504
+ framework .ExpectNoError (err )
505
+
506
+ evictedPods := sets .New [string ]()
507
+ for _ , pod := range podList .Items {
508
+ e := & policyv1.Eviction {
509
+ ObjectMeta : metav1.ObjectMeta {
510
+ Name : pod .Name ,
511
+ Namespace : ns ,
512
+ },
513
+ }
514
+ err = cs .CoreV1 ().Pods (ns ).EvictV1 (ctx , e )
515
+ if err == nil {
516
+ evictedPods .Insert (pod .Name )
517
+ }
518
+ }
519
+ gomega .Expect (evictedPods ).Should (gomega .HaveLen (tc .expectedSuccesfulPodEvictions ))
520
+ _ , err = e2epod .WaitForPods (ctx , cs , ns , metav1.ListOptions {}, e2epod.Range {NoneMatching : true }, framework .PodDeleteTimeout , "evicted pods should be deleted" , func (pod * v1.Pod ) bool {
521
+ return evictedPods .Has (pod .Name ) && pod .DeletionTimestamp == nil
522
+ })
523
+ framework .ExpectNoError (err )
524
+ })
525
+ }
417
526
})
418
527
419
528
func createPDBMinAvailableOrDie (ctx context.Context , cs kubernetes.Interface , ns string , name string , minAvailable intstr.IntOrString , labels map [string ]string ) {
@@ -433,15 +542,16 @@ func createPDBMinAvailableOrDie(ctx context.Context, cs kubernetes.Interface, ns
433
542
waitForPdbToBeProcessed (ctx , cs , ns , name )
434
543
}
435
544
436
- func createPDBMaxUnavailableOrDie (ctx context.Context , cs kubernetes.Interface , ns string , name string , maxUnavailable intstr.IntOrString ) {
545
+ func createPDBMaxUnavailableOrDie (ctx context.Context , cs kubernetes.Interface , ns string , name string , maxUnavailable intstr.IntOrString , unhealthyPodEvictionPolicy * policyv1. UnhealthyPodEvictionPolicyType ) {
437
546
pdb := policyv1.PodDisruptionBudget {
438
547
ObjectMeta : metav1.ObjectMeta {
439
548
Name : name ,
440
549
Namespace : ns ,
441
550
},
442
551
Spec : policyv1.PodDisruptionBudgetSpec {
443
- Selector : & metav1.LabelSelector {MatchLabels : map [string ]string {"foo" : "bar" }},
444
- MaxUnavailable : & maxUnavailable ,
552
+ Selector : & metav1.LabelSelector {MatchLabels : map [string ]string {"foo" : "bar" }},
553
+ MaxUnavailable : & maxUnavailable ,
554
+ UnhealthyPodEvictionPolicy : unhealthyPodEvictionPolicy ,
445
555
},
446
556
}
447
557
_ , err := cs .PolicyV1 ().PodDisruptionBudgets (ns ).Create (ctx , & pdb , metav1.CreateOptions {})
@@ -670,7 +780,7 @@ func waitForPdbToBeDeleted(ctx context.Context, cs kubernetes.Interface, ns stri
670
780
framework .ExpectNoError (err , "Waiting for the pdb to be deleted in namespace %s" , ns )
671
781
}
672
782
673
- func waitForPdbToObserveHealthyPods (ctx context.Context , cs kubernetes.Interface , ns string , healthyCount int32 ) {
783
+ func waitForPdbToObserveHealthyPods (ctx context.Context , cs kubernetes.Interface , ns string , healthyCount , desiredHealthy int32 ) {
674
784
ginkgo .By ("Waiting for the pdb to observed all healthy pods" )
675
785
err := wait .PollUntilContextTimeout (ctx , framework .Poll , wait .ForeverTestTimeout , true , func (ctx context.Context ) (bool , error ) {
676
786
pdb , err := cs .PolicyV1 ().PodDisruptionBudgets (ns ).Get (ctx , "foo" , metav1.GetOptions {})
@@ -680,6 +790,9 @@ func waitForPdbToObserveHealthyPods(ctx context.Context, cs kubernetes.Interface
680
790
if pdb .Status .CurrentHealthy != healthyCount {
681
791
return false , nil
682
792
}
793
+ if pdb .Status .DesiredHealthy != desiredHealthy {
794
+ return false , nil
795
+ }
683
796
return true , nil
684
797
})
685
798
framework .ExpectNoError (err , "Waiting for the pdb in namespace %s to observed %d healthy pods" , ns , healthyCount )
0 commit comments