Skip to content

Commit b779fb8

Browse files
committed
add e2e tests for UnhealthyPodEvictionPolicy
1 parent 68d3458 commit b779fb8

File tree

1 file changed

+119
-6
lines changed

1 file changed

+119
-6
lines changed

test/e2e/apps/disruption.go

Lines changed: 119 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,19 @@ import (
3838
"k8s.io/apimachinery/pkg/types"
3939
"k8s.io/apimachinery/pkg/util/intstr"
4040
"k8s.io/apimachinery/pkg/util/json"
41+
"k8s.io/apimachinery/pkg/util/sets"
4142
"k8s.io/apimachinery/pkg/util/wait"
4243
"k8s.io/client-go/dynamic"
4344
"k8s.io/client-go/kubernetes"
4445
clientscheme "k8s.io/client-go/kubernetes/scheme"
4546
"k8s.io/client-go/util/retry"
4647
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
4748
"k8s.io/kubernetes/test/e2e/framework"
49+
e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
4850
e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
4951
imageutils "k8s.io/kubernetes/test/utils/image"
5052
admissionapi "k8s.io/pod-security-admission/api"
53+
"k8s.io/utils/ptr"
5154
)
5255

5356
// schedulingTimeout is longer specifically because sometimes we need to wait
@@ -301,7 +304,7 @@ var _ = SIGDescribe("DisruptionController", func() {
301304
}
302305

303306
if c.maxUnavailable.String() != "" {
304-
createPDBMaxUnavailableOrDie(ctx, cs, ns, defaultName, c.maxUnavailable)
307+
createPDBMaxUnavailableOrDie(ctx, cs, ns, defaultName, c.maxUnavailable, nil)
305308
}
306309

307310
// Locate a running pod.
@@ -375,7 +378,7 @@ var _ = SIGDescribe("DisruptionController", func() {
375378

376379
ginkgo.By("Trying to evict the same pod we tried earlier which should now be evictable")
377380
waitForPodsOrDie(ctx, cs, ns, 3)
378-
waitForPdbToObserveHealthyPods(ctx, cs, ns, 3)
381+
waitForPdbToObserveHealthyPods(ctx, cs, ns, 3, 2)
379382
err = cs.CoreV1().Pods(ns).EvictV1(ctx, e)
380383
framework.ExpectNoError(err) // the eviction is now allowed
381384

@@ -414,6 +417,112 @@ var _ = SIGDescribe("DisruptionController", func() {
414417
framework.ExpectNoError(err) // the eviction is now allowed
415418
})
416419

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+
}
417526
})
418527

419528
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
433542
waitForPdbToBeProcessed(ctx, cs, ns, name)
434543
}
435544

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) {
437546
pdb := policyv1.PodDisruptionBudget{
438547
ObjectMeta: metav1.ObjectMeta{
439548
Name: name,
440549
Namespace: ns,
441550
},
442551
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,
445555
},
446556
}
447557
_, err := cs.PolicyV1().PodDisruptionBudgets(ns).Create(ctx, &pdb, metav1.CreateOptions{})
@@ -670,7 +780,7 @@ func waitForPdbToBeDeleted(ctx context.Context, cs kubernetes.Interface, ns stri
670780
framework.ExpectNoError(err, "Waiting for the pdb to be deleted in namespace %s", ns)
671781
}
672782

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) {
674784
ginkgo.By("Waiting for the pdb to observed all healthy pods")
675785
err := wait.PollUntilContextTimeout(ctx, framework.Poll, wait.ForeverTestTimeout, true, func(ctx context.Context) (bool, error) {
676786
pdb, err := cs.PolicyV1().PodDisruptionBudgets(ns).Get(ctx, "foo", metav1.GetOptions{})
@@ -680,6 +790,9 @@ func waitForPdbToObserveHealthyPods(ctx context.Context, cs kubernetes.Interface
680790
if pdb.Status.CurrentHealthy != healthyCount {
681791
return false, nil
682792
}
793+
if pdb.Status.DesiredHealthy != desiredHealthy {
794+
return false, nil
795+
}
683796
return true, nil
684797
})
685798
framework.ExpectNoError(err, "Waiting for the pdb in namespace %s to observed %d healthy pods", ns, healthyCount)

0 commit comments

Comments
 (0)