diff --git a/test/e2e/framework/pod/utils.go b/test/e2e/framework/pod/utils.go index 7e9c68b5d09..25188e7b9c0 100644 --- a/test/e2e/framework/pod/utils.go +++ b/test/e2e/framework/pod/utils.go @@ -43,7 +43,7 @@ const InfiniteSleepCommand = "trap exit TERM; while true; do sleep 1; done" // // This is useful for testing scenarios where the container is terminated // with a non-zero exit code. -const InfiniteSleepCommandWithoutGracefulShutdown = "while true; do sleep 1; done" +const InfiniteSleepCommandWithoutGracefulShutdown = "while true; do sleep 100000; done" // GenerateScriptCmd generates the corresponding command lines to execute a command. func GenerateScriptCmd(command string) []string { diff --git a/test/e2e/framework/pod/wait.go b/test/e2e/framework/pod/wait.go index 189d0f38d32..6d368de6beb 100644 --- a/test/e2e/framework/pod/wait.go +++ b/test/e2e/framework/pod/wait.go @@ -84,6 +84,27 @@ func BeRunningNoRetries() types.GomegaMatcher { ) } +// BeRunningReadyNoRetries verifies that a pod starts running and has a ready +// condition of status true. It's a permanent failure when the pod enters some +// other permanent phase. +func BeRunningReadyNoRetries() types.GomegaMatcher { + return gomega.And( + // This additional matcher checks for the final error condition. + gcustom.MakeMatcher(func(pod *v1.Pod) (bool, error) { + switch pod.Status.Phase { + case v1.PodFailed, v1.PodSucceeded: + return false, gomega.StopTrying(fmt.Sprintf("Expected pod to reach phase %q, got final phase %q instead:\n%s", v1.PodRunning, pod.Status.Phase, format.Object(pod, 1))) + default: + return true, nil + } + }), + BeInPhase(v1.PodRunning), + gcustom.MakeMatcher(func(pod *v1.Pod) (bool, error) { + return podutils.IsPodReady(pod), nil + }).WithMessage("Expected pod to have a ready condition of status true"), + ) +} + // BeInPhase matches if pod.status.phase is the expected phase. func BeInPhase(phase v1.PodPhase) types.GomegaMatcher { // A simple implementation of this would be: @@ -526,6 +547,16 @@ func WaitTimeoutForPodRunningInNamespace(ctx context.Context, c clientset.Interf Should(BeRunningNoRetries()) } +// WaitTimeoutForPodRunningReadyInNamespace waits the given timeout duration for the specified pod to become running +// and have a ready condition of status true. +// It does not need to exist yet when this function gets called and the pod is not expected to be recreated +// when it succeeds or fails. +func WaitTimeoutForPodRunningReadyInNamespace(ctx context.Context, c clientset.Interface, podName, namespace string, timeout time.Duration) error { + return framework.Gomega().Eventually(ctx, framework.RetryNotFound(framework.GetObject(c.CoreV1().Pods(namespace).Get, podName, metav1.GetOptions{}))). + WithTimeout(timeout). + Should(BeRunningReadyNoRetries()) +} + // WaitForPodRunningInNamespace waits default amount of time (podStartTimeout) for the specified pod to become running. // Returns an error if timeout occurs first, or pod goes in to failed state. func WaitForPodRunningInNamespace(ctx context.Context, c clientset.Interface, pod *v1.Pod) error { diff --git a/test/e2e/storage/testsuites/subpath.go b/test/e2e/storage/testsuites/subpath.go index 34d35e53768..a0103b287e1 100644 --- a/test/e2e/storage/testsuites/subpath.go +++ b/test/e2e/storage/testsuites/subpath.go @@ -806,8 +806,8 @@ func testPodContainerRestartWithHooks(ctx context.Context, f *framework.Framewor pod, err := f.ClientSet.CoreV1().Pods(f.Namespace.Name).Create(ctx, pod, metav1.CreateOptions{}) framework.ExpectNoError(err, "while creating pod") ginkgo.DeferCleanup(e2epod.DeletePodWithWait, f.ClientSet, pod) - err = e2epod.WaitTimeoutForPodRunningInNamespace(ctx, f.ClientSet, pod.Name, pod.Namespace, f.Timeouts.PodStart) - framework.ExpectNoError(err, "while waiting for pod to be running") + err = e2epod.WaitTimeoutForPodRunningReadyInNamespace(ctx, f.ClientSet, pod.Name, pod.Namespace, f.Timeouts.PodStart) + framework.ExpectNoError(err, "while waiting for pod to be running and ready") ginkgo.By("Failing liveness probe") hooks.FailLivenessProbe(pod, probeFilePath)