@@ -32,6 +32,7 @@ import (
3232 e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
3333 imageutils "k8s.io/kubernetes/test/utils/image"
3434 admissionapi "k8s.io/pod-security-admission/api"
35+ "k8s.io/utils/ptr"
3536
3637 "github.com/onsi/ginkgo/v2"
3738 "github.com/onsi/gomega"
@@ -551,7 +552,7 @@ func validDuration(duration time.Duration, low, high int64) bool {
551552 return duration >= time .Second * time .Duration (low ) && duration <= time .Second * time .Duration (high )
552553}
553554
554- var _ = SIGDescribe (feature . PodLifecycleSleepAction , func () {
555+ var _ = SIGDescribe ("Lifecycle Sleep Hook" , func () {
555556 f := framework .NewDefaultFramework ("pod-lifecycle-sleep-action" )
556557 f .NamespacePodSecurityLevel = admissionapi .LevelBaseline
557558 var podClient * e2epod.PodClient
@@ -560,73 +561,148 @@ var _ = SIGDescribe(feature.PodLifecycleSleepAction, func() {
560561 ginkgo .BeforeEach (func (ctx context.Context ) {
561562 podClient = e2epod .NewPodClient (f )
562563 })
564+
565+ var finalizer = "test/finalizer"
566+ /*
567+ Release : v1.34
568+ Testname: Pod Lifecycle, prestop sleep hook
569+ Description: When a pre-stop handler is specified in the container lifecycle using a 'Sleep' action, then the handler MUST be invoked before the container is terminated. A test pod will be created to verify if its termination time aligns with the sleep time specified when it is terminated.
570+ */
563571 ginkgo .It ("valid prestop hook using sleep action" , func (ctx context.Context ) {
572+ const sleepSeconds = 50
573+ const gracePeriod = 100
564574 lifecycle := & v1.Lifecycle {
565575 PreStop : & v1.LifecycleHandler {
566- Sleep : & v1.SleepAction {Seconds : 5 },
576+ Sleep : & v1.SleepAction {Seconds : sleepSeconds },
567577 },
568578 }
569- podWithHook := getPodWithHook ("pod-with-prestop-sleep-hook" , imageutils .GetPauseImageName (), lifecycle )
579+ name := "pod-with-prestop-sleep-hook"
580+ podWithHook := getPodWithHook (name , imageutils .GetPauseImageName (), lifecycle )
581+ podWithHook .Finalizers = append (podWithHook .Finalizers , finalizer )
582+ podWithHook .Spec .TerminationGracePeriodSeconds = ptr.To [int64 ](gracePeriod )
570583 ginkgo .By ("create the pod with lifecycle hook using sleep action" )
571- podClient .CreateSync (ctx , podWithHook )
584+ p := podClient .CreateSync (ctx , podWithHook )
585+ defer podClient .RemoveFinalizer (ctx , name , finalizer )
572586 ginkgo .By ("delete the pod with lifecycle hook using sleep action" )
573- start := time .Now ()
574- podClient .DeleteSync (ctx , podWithHook .Name , metav1.DeleteOptions {}, f .Timeouts .PodDelete )
575- cost := time .Since (start )
576- // cost should be
577- // longer than 5 seconds (pod should sleep for 5 seconds)
578- // shorter than gracePeriodSeconds (default 30 seconds here)
579- if ! validDuration (cost , 5 , 30 ) {
580- framework .Failf ("unexpected delay duration before killing the pod, cost = %v" , cost )
587+ _ = podClient .Delete (ctx , podWithHook .Name , metav1.DeleteOptions {})
588+ p , err := podClient .Get (ctx , p .Name , metav1.GetOptions {})
589+ if err != nil {
590+ framework .Failf ("failed getting pod after deletion" )
591+ }
592+ // deletionTimestamp equals to delete_time + tgps
593+ // TODO: reduce sleep_seconds and tgps after issues.k8s.io/132205 is solved
594+ // we get deletionTimestamp before container become terminated here because of issues.k8s.io/132205
595+ deletionTS := p .DeletionTimestamp .Time
596+ if err := e2epod .WaitForContainerTerminated (ctx , f .ClientSet , p .Namespace , p .Name , name , sleepSeconds * 2 * time .Second ); err != nil {
597+ framework .Failf ("failed waiting for container terminated" )
598+ }
599+
600+ p , err = podClient .Get (ctx , p .Name , metav1.GetOptions {})
601+ if err != nil {
602+ framework .Failf ("failed getting pod after deletion" )
603+ }
604+ // finishAt equals to delete_time + sleep_duration
605+ finishAt := p .Status .ContainerStatuses [0 ].State .Terminated .FinishedAt
606+
607+ // sleep_duration = (delete_time + sleep_duration) - (delete_time + tgps) + tgps
608+ sleepDuration := finishAt .Sub (deletionTS ) + time .Second * gracePeriod
609+
610+ // sleep_duration should be
611+ // longer than 50 seconds (pod should sleep for 50 seconds)
612+ // shorter than gracePeriodSeconds (100 seconds here)
613+ if ! validDuration (sleepDuration , sleepSeconds , gracePeriod ) {
614+ framework .Failf ("unexpected delay duration before killing the pod, finishAt = %v, deletionAt= %v" , finishAt , deletionTS )
581615 }
582616 })
583617
618+ /*
619+ Release : v1.34
620+ Testname: Pod Lifecycle, prestop sleep hook with low gracePeriodSeconds
621+ Description: When a pre-stop handler is specified in the container lifecycle using a 'Sleep' action, then the handler MUST be invoked before the container is terminated. A test pod will be created, and its `gracePeriodSeconds` will be modified to a value less than the sleep time before termination. The termination time will then be checked to ensure it aligns with the `gracePeriodSeconds` value.
622+ */
584623 ginkgo .It ("reduce GracePeriodSeconds during runtime" , func (ctx context.Context ) {
624+ const sleepSeconds = 50
585625 lifecycle := & v1.Lifecycle {
586626 PreStop : & v1.LifecycleHandler {
587- Sleep : & v1.SleepAction {Seconds : 15 },
627+ Sleep : & v1.SleepAction {Seconds : sleepSeconds },
588628 },
589629 }
590- podWithHook := getPodWithHook ("pod-with-prestop-sleep-hook" , imageutils .GetPauseImageName (), lifecycle )
630+ name := "pod-with-prestop-sleep-hook"
631+ podWithHook := getPodWithHook (name , imageutils .GetPauseImageName (), lifecycle )
632+ podWithHook .Finalizers = append (podWithHook .Finalizers , finalizer )
633+ podWithHook .Spec .TerminationGracePeriodSeconds = ptr.To [int64 ](100 )
591634 ginkgo .By ("create the pod with lifecycle hook using sleep action" )
592- podClient .CreateSync (ctx , podWithHook )
635+ p := podClient .CreateSync (ctx , podWithHook )
636+ defer podClient .RemoveFinalizer (ctx , name , finalizer )
593637 ginkgo .By ("delete the pod with lifecycle hook using sleep action" )
594- start := time .Now ()
595- podClient .DeleteSync (ctx , podWithHook .Name , * metav1 .NewDeleteOptions (2 ), f .Timeouts .PodDelete )
596- cost := time .Since (start )
597- // cost should be
598- // longer than 2 seconds (we change gracePeriodSeconds to 2 seconds here, and it's less than sleep action)
638+
639+ const gracePeriod = 30
640+ _ = podClient .Delete (ctx , podWithHook .Name , * metav1 .NewDeleteOptions (gracePeriod ))
641+ p , err := podClient .Get (ctx , p .Name , metav1.GetOptions {})
642+ if err != nil {
643+ framework .Failf ("failed getting pod after deletion" )
644+ }
645+ // deletionTimestamp equals to delete_time + tgps
646+ // TODO: reduce sleep_seconds and tgps after issues.k8s.io/132205 is solved
647+ // we get deletionTimestamp before container become terminated here because of issues.k8s.io/132205
648+ deletionTS := p .DeletionTimestamp .Time
649+ if err := e2epod .WaitForContainerTerminated (ctx , f .ClientSet , p .Namespace , p .Name , name , sleepSeconds * 2 * time .Second ); err != nil {
650+ framework .Failf ("failed waiting for container terminated" )
651+ }
652+ p , err = podClient .Get (ctx , p .Name , metav1.GetOptions {})
653+ if err != nil {
654+ framework .Failf ("failed getting pod after deletion" )
655+ }
656+ // finishAt equals to delete_time + sleep_duration
657+ finishAt := p .Status .ContainerStatuses [0 ].State .Terminated .FinishedAt
658+
659+ // sleep_duration = (delete_time + sleep_duration) - (delete_time + tgps) + tgps
660+ sleepDuration := finishAt .Sub (deletionTS ) + time .Second * gracePeriod
661+ // sleep_duration should be
662+ // longer than 30 seconds (we change gracePeriodSeconds to 30 seconds here, and it's less than sleep action)
599663 // shorter than sleep action (to make sure it doesn't take effect)
600- if ! validDuration (cost , 2 , 15 ) {
601- framework .Failf ("unexpected delay duration before killing the pod, cost = %v" , cost )
664+ if ! validDuration (sleepDuration , gracePeriod , sleepSeconds ) {
665+ framework .Failf ("unexpected delay duration before killing the pod, finishAt = %v, deletionAt= %v " , finishAt , deletionTS )
602666 }
603667 })
604668
669+ /*
670+ Release : v1.34
671+ Testname: Pod Lifecycle, prestop sleep hook with erroneous startup command
672+ Description: When a pre-stop handler is specified in the container lifecycle using a 'Sleep' action, then the handler MUST be invoked before the container is terminated. A test pod with an erroneous startup command will be created, and upon termination, it will be checked whether it ignored the sleep time.
673+ */
605674 ginkgo .It ("ignore terminated container" , func (ctx context.Context ) {
675+ const sleepSeconds = 10
676+ const gracePeriod = 30
606677 lifecycle := & v1.Lifecycle {
607678 PreStop : & v1.LifecycleHandler {
608- Sleep : & v1.SleepAction {Seconds : 20 },
679+ Sleep : & v1.SleepAction {Seconds : sleepSeconds },
609680 },
610681 }
611682 name := "pod-with-prestop-sleep-hook"
612683 podWithHook := getPodWithHook (name , imageutils .GetE2EImage (imageutils .BusyBox ), lifecycle )
684+ podWithHook .Spec .TerminationGracePeriodSeconds = ptr.To [int64 ](gracePeriod )
613685 podWithHook .Spec .Containers [0 ].Command = []string {"/bin/sh" }
614686 podWithHook .Spec .Containers [0 ].Args = []string {"-c" , "exit 0" }
615687 podWithHook .Spec .RestartPolicy = v1 .RestartPolicyNever
616688 ginkgo .By ("create the pod with lifecycle hook using sleep action" )
617689 p := podClient .Create (ctx , podWithHook )
618- framework .ExpectNoError (e2epod .WaitForContainerTerminated (ctx , f .ClientSet , f .Namespace .Name , p .Name , name , 3 * time .Minute ))
619- ginkgo .By ("delete the pod with lifecycle hook using sleep action" )
620- start := time .Now ()
621- podClient .DeleteSync (ctx , podWithHook .Name , metav1.DeleteOptions {}, f .Timeouts .PodDelete )
622- cost := time .Since (start )
690+ defer podClient .DeleteSync (ctx , podWithHook .Name , metav1.DeleteOptions {}, f .Timeouts .PodDelete )
691+ framework .ExpectNoError (e2epod .WaitForContainerTerminated (ctx , f .ClientSet , f .Namespace .Name , p .Name , name , gracePeriod * time .Second ))
692+
693+ p , err := podClient .Get (ctx , p .Name , metav1.GetOptions {})
694+ if err != nil {
695+ framework .Failf ("failed getting pod after deletion" )
696+ }
697+ finishAt := p .Status .ContainerStatuses [0 ].State .Terminated .FinishedAt
698+ startedAt := p .Status .ContainerStatuses [0 ].State .Terminated .StartedAt
699+ cost := finishAt .Sub (startedAt .Time )
623700 // cost should be
624701 // shorter than sleep action (container is terminated and sleep action should be ignored)
625- if ! validDuration (cost , 0 , 15 ) {
702+ if ! validDuration (cost , 0 , sleepSeconds ) {
626703 framework .Failf ("unexpected delay duration before killing the pod, cost = %v" , cost )
627704 }
628705 })
629-
630706 })
631707})
632708
0 commit comments