1
16
17 package pod
18
19 import (
20 "context"
21 "errors"
22 "fmt"
23 "reflect"
24 "strings"
25 "time"
26
27 "github.com/onsi/ginkgo/v2"
28 "github.com/onsi/gomega"
29 "github.com/onsi/gomega/gcustom"
30 "github.com/onsi/gomega/types"
31
32 appsv1 "k8s.io/api/apps/v1"
33 v1 "k8s.io/api/core/v1"
34 apierrors "k8s.io/apimachinery/pkg/api/errors"
35 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
36 "k8s.io/apimachinery/pkg/labels"
37 apitypes "k8s.io/apimachinery/pkg/types"
38 clientset "k8s.io/client-go/kubernetes"
39 "k8s.io/kubectl/pkg/util/podutils"
40 "k8s.io/kubernetes/test/e2e/framework"
41 testutils "k8s.io/kubernetes/test/utils"
42 "k8s.io/kubernetes/test/utils/format"
43 )
44
45 const (
46
47 defaultPodDeletionTimeout = 3 * time.Minute
48
49
50 podListTimeout = time.Minute
51
52 podRespondingTimeout = 15 * time.Minute
53
54
55 podScheduledBeforeTimeout = podListTimeout + (20 * time.Second)
56
57
58 podStartTimeout = 5 * time.Minute
59
60
61
62 singleCallTimeout = 5 * time.Minute
63
64
65 slowPodStartTimeout = 15 * time.Minute
66 )
67
68 type podCondition func(pod *v1.Pod) (bool, error)
69
70
71
72 func BeRunningNoRetries() types.GomegaMatcher {
73 return gomega.And(
74
75 gcustom.MakeMatcher(func(pod *v1.Pod) (bool, error) {
76 switch pod.Status.Phase {
77 case v1.PodFailed, v1.PodSucceeded:
78 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)))
79 default:
80 return true, nil
81 }
82 }),
83 BeInPhase(v1.PodRunning),
84 )
85 }
86
87
88 func BeInPhase(phase v1.PodPhase) types.GomegaMatcher {
89
90
91
92
93
94
95
96
97 return gcustom.MakeMatcher(func(pod *v1.Pod) (bool, error) {
98 return pod.Status.Phase == phase, nil
99 }).WithTemplate("Expected Pod {{.To}} be in {{format .Data}}\nGot instead:\n{{.FormattedActual}}").WithTemplateData(phase)
100 }
101
102
103
104
105
106
107
108
109
110
111
112 func WaitForPodsRunningReady(ctx context.Context, c clientset.Interface, ns string, minPods, allowedNotReadyPods int32, timeout time.Duration) error {
113 if minPods == -1 || allowedNotReadyPods == -1 {
114 return nil
115 }
116
117
118
119
120
121
122
123 type state struct {
124 ReplicationControllers []v1.ReplicationController
125 ReplicaSets []appsv1.ReplicaSet
126 Pods []v1.Pod
127 }
128
129
130
131
132 notReady := int32(-1)
133
134 err := framework.Gomega().Eventually(ctx, framework.HandleRetry(func(ctx context.Context) (*state, error) {
135
136 notReady = -1
137
138 rcList, err := c.CoreV1().ReplicationControllers(ns).List(ctx, metav1.ListOptions{})
139 if err != nil {
140 return nil, fmt.Errorf("listing replication controllers in namespace %s: %w", ns, err)
141 }
142 rsList, err := c.AppsV1().ReplicaSets(ns).List(ctx, metav1.ListOptions{})
143 if err != nil {
144 return nil, fmt.Errorf("listing replication sets in namespace %s: %w", ns, err)
145 }
146 podList, err := c.CoreV1().Pods(ns).List(ctx, metav1.ListOptions{})
147 if err != nil {
148 return nil, fmt.Errorf("listing pods in namespace %s: %w", ns, err)
149 }
150 return &state{
151 ReplicationControllers: rcList.Items,
152 ReplicaSets: rsList.Items,
153 Pods: podList.Items,
154 }, nil
155 })).WithTimeout(timeout).Should(framework.MakeMatcher(func(s *state) (func() string, error) {
156 replicas, replicaOk := int32(0), int32(0)
157 for _, rc := range s.ReplicationControllers {
158 replicas += *rc.Spec.Replicas
159 replicaOk += rc.Status.ReadyReplicas
160 }
161 for _, rs := range s.ReplicaSets {
162 replicas += *rs.Spec.Replicas
163 replicaOk += rs.Status.ReadyReplicas
164 }
165
166 nOk := int32(0)
167 notReady = int32(0)
168 failedPods := []v1.Pod{}
169 otherPods := []v1.Pod{}
170 succeededPods := []string{}
171 for _, pod := range s.Pods {
172 res, err := testutils.PodRunningReady(&pod)
173 switch {
174 case res && err == nil:
175 nOk++
176 case pod.Status.Phase == v1.PodSucceeded:
177
178 succeededPods = append(succeededPods, pod.Name)
179 case pod.Status.Phase == v1.PodFailed:
180
181 if metav1.GetControllerOf(&pod) == nil {
182 failedPods = append(failedPods, pod)
183 }
184 default:
185 notReady++
186 otherPods = append(otherPods, pod)
187 }
188 }
189 done := replicaOk == replicas && nOk >= minPods && (len(failedPods)+len(otherPods)) == 0
190 if done {
191 return nil, nil
192 }
193
194
195 return func() string {
196 var buffer strings.Builder
197 buffer.WriteString(fmt.Sprintf("Expected all pods (need at least %d) in namespace %q to be running and ready (except for %d).\n", minPods, ns, allowedNotReadyPods))
198 buffer.WriteString(fmt.Sprintf("%d / %d pods were running and ready.\n", nOk, len(s.Pods)))
199 buffer.WriteString(fmt.Sprintf("Expected %d pod replicas, %d are Running and Ready.\n", replicas, replicaOk))
200 if len(succeededPods) > 0 {
201 buffer.WriteString(fmt.Sprintf("Pods that completed successfully:\n%s", format.Object(succeededPods, 1)))
202 }
203 if len(failedPods) > 0 {
204 buffer.WriteString(fmt.Sprintf("Pods that failed and were not controlled by some controller:\n%s", format.Object(failedPods, 1)))
205 }
206 if len(otherPods) > 0 {
207 buffer.WriteString(fmt.Sprintf("Pods that were neither completed nor running:\n%s", format.Object(otherPods, 1)))
208 }
209 return buffer.String()
210 }, nil
211 }))
212
213
214 if err != nil && notReady >= 0 && notReady <= allowedNotReadyPods {
215 framework.Logf("Number of not-ready pods (%d) is below the allowed threshold (%d).", notReady, allowedNotReadyPods)
216 return nil
217 }
218 return err
219 }
220
221
222
223 func WaitForPodCondition(ctx context.Context, c clientset.Interface, ns, podName, conditionDesc string, timeout time.Duration, condition podCondition) error {
224 return framework.Gomega().
225 Eventually(ctx, framework.RetryNotFound(framework.GetObject(c.CoreV1().Pods(ns).Get, podName, metav1.GetOptions{}))).
226 WithTimeout(timeout).
227 Should(framework.MakeMatcher(func(pod *v1.Pod) (func() string, error) {
228 done, err := condition(pod)
229 if err != nil {
230 return nil, err
231 }
232 if done {
233 return nil, nil
234 }
235 return func() string {
236 return fmt.Sprintf("expected pod to be %s, got instead:\n%s", conditionDesc, format.Object(pod, 1))
237 }, nil
238 }))
239 }
240
241
242
243
244 type Range struct {
245
246 MinMatching int
247
248
249 MaxMatching int
250
251 NoneMatching bool
252
253 AllMatching bool
254
255 MinFound int
256 }
257
258
259 func (r Range) Min() int {
260 min := r.MinMatching
261 if min < r.MinFound {
262 min = r.MinFound
263 }
264 return min
265 }
266
267
268
269
270
271
272 func WaitForPods(ctx context.Context, c clientset.Interface, ns string, opts metav1.ListOptions, r Range, timeout time.Duration, conditionDesc string, condition func(*v1.Pod) bool) (*v1.PodList, error) {
273 var finalPods *v1.PodList
274 minPods := r.Min()
275 match := func(pods *v1.PodList) (func() string, error) {
276 finalPods = pods
277
278 if len(pods.Items) < minPods {
279 return func() string {
280 return fmt.Sprintf("expected at least %d pods, only got %d", minPods, len(pods.Items))
281 }, nil
282 }
283
284 var nonMatchingPods, matchingPods []v1.Pod
285 for _, pod := range pods.Items {
286 if condition(&pod) {
287 matchingPods = append(matchingPods, pod)
288 } else {
289 nonMatchingPods = append(nonMatchingPods, pod)
290 }
291 }
292 matching := len(pods.Items) - len(nonMatchingPods)
293 if matching < r.MinMatching && r.MinMatching > 0 {
294 return func() string {
295 return fmt.Sprintf("expected at least %d pods to %s, %d out of %d were not:\n%s",
296 r.MinMatching, conditionDesc, len(nonMatchingPods), len(pods.Items),
297 format.Object(nonMatchingPods, 1))
298 }, nil
299 }
300 if len(nonMatchingPods) > 0 && r.AllMatching {
301 return func() string {
302 return fmt.Sprintf("expected all pods to %s, %d out of %d were not:\n%s",
303 conditionDesc, len(nonMatchingPods), len(pods.Items),
304 format.Object(nonMatchingPods, 1))
305 }, nil
306 }
307 if matching > r.MaxMatching && r.MaxMatching > 0 {
308 return func() string {
309 return fmt.Sprintf("expected at most %d pods to %s, %d out of %d were:\n%s",
310 r.MinMatching, conditionDesc, len(matchingPods), len(pods.Items),
311 format.Object(matchingPods, 1))
312 }, nil
313 }
314 if matching > 0 && r.NoneMatching {
315 return func() string {
316 return fmt.Sprintf("expected no pods to %s, %d out of %d were:\n%s",
317 conditionDesc, len(matchingPods), len(pods.Items),
318 format.Object(matchingPods, 1))
319 }, nil
320 }
321 return nil, nil
322 }
323
324 err := framework.Gomega().
325 Eventually(ctx, framework.ListObjects(c.CoreV1().Pods(ns).List, opts)).
326 WithTimeout(timeout).
327 Should(framework.MakeMatcher(match))
328 return finalPods, err
329 }
330
331
332
333 func RunningReady(p *v1.Pod) bool {
334 return p.Status.Phase == v1.PodRunning && podutils.IsPodReady(p)
335 }
336
337
338 func WaitForPodsRunning(ctx context.Context, c clientset.Interface, ns string, num int, timeout time.Duration) error {
339 _, err := WaitForPods(ctx, c, ns, metav1.ListOptions{}, Range{MinMatching: num, MaxMatching: num}, timeout,
340 "be running and ready", func(pod *v1.Pod) bool {
341 ready, _ := testutils.PodRunningReady(pod)
342 return ready
343 })
344 return err
345 }
346
347
348 func WaitForPodsSchedulingGated(ctx context.Context, c clientset.Interface, ns string, num int, timeout time.Duration) error {
349 _, err := WaitForPods(ctx, c, ns, metav1.ListOptions{}, Range{MinMatching: num, MaxMatching: num}, timeout,
350 "be in scheduling gated state", func(pod *v1.Pod) bool {
351 for _, condition := range pod.Status.Conditions {
352 if condition.Type == v1.PodScheduled && condition.Reason == v1.PodReasonSchedulingGated {
353 return true
354 }
355 }
356 return false
357 })
358 return err
359 }
360
361
362
363 func WaitForPodsWithSchedulingGates(ctx context.Context, c clientset.Interface, ns string, num int, timeout time.Duration, schedulingGates []v1.PodSchedulingGate) error {
364 _, err := WaitForPods(ctx, c, ns, metav1.ListOptions{}, Range{MinMatching: num, MaxMatching: num}, timeout,
365 "have certain scheduling gates", func(pod *v1.Pod) bool {
366 return reflect.DeepEqual(pod.Spec.SchedulingGates, schedulingGates)
367 })
368 return err
369 }
370
371
372
373
374
375
376 func WaitForPodTerminatedInNamespace(ctx context.Context, c clientset.Interface, podName, reason, namespace string) error {
377 return WaitForPodCondition(ctx, c, namespace, podName, fmt.Sprintf("terminated with reason %s", reason), podStartTimeout, func(pod *v1.Pod) (bool, error) {
378
379
380 if pod.Status.Phase == v1.PodFailed {
381 if pod.Status.Reason == reason {
382 return true, nil
383 }
384 return true, fmt.Errorf("Expected pod %q in namespace %q to be terminated with reason %q, got reason: %q", podName, namespace, reason, pod.Status.Reason)
385 }
386 return false, nil
387 })
388 }
389
390
391 func WaitForPodTerminatingInNamespaceTimeout(ctx context.Context, c clientset.Interface, podName, namespace string, timeout time.Duration) error {
392 return WaitForPodCondition(ctx, c, namespace, podName, "is terminating", timeout, func(pod *v1.Pod) (bool, error) {
393 if pod.DeletionTimestamp != nil {
394 return true, nil
395 }
396 return false, nil
397 })
398 }
399
400
401 func WaitForPodSuccessInNamespaceTimeout(ctx context.Context, c clientset.Interface, podName, namespace string, timeout time.Duration) error {
402 return WaitForPodCondition(ctx, c, namespace, podName, fmt.Sprintf("%s or %s", v1.PodSucceeded, v1.PodFailed), timeout, func(pod *v1.Pod) (bool, error) {
403 if pod.DeletionTimestamp == nil && pod.Spec.RestartPolicy == v1.RestartPolicyAlways {
404 return true, gomega.StopTrying(fmt.Sprintf("pod %q will never terminate with a succeeded state since its restart policy is Always", podName))
405 }
406 switch pod.Status.Phase {
407 case v1.PodSucceeded:
408 ginkgo.By("Saw pod success")
409 return true, nil
410 case v1.PodFailed:
411 return true, gomega.StopTrying(fmt.Sprintf("pod %q failed with status: %+v", podName, pod.Status))
412 default:
413 return false, nil
414 }
415 })
416 }
417
418
419
420
421
422 func WaitForPodNameUnschedulableInNamespace(ctx context.Context, c clientset.Interface, podName, namespace string) error {
423 return WaitForPodCondition(ctx, c, namespace, podName, v1.PodReasonUnschedulable, podStartTimeout, func(pod *v1.Pod) (bool, error) {
424
425
426 if pod.Status.Phase == v1.PodPending {
427 for _, cond := range pod.Status.Conditions {
428 if cond.Type == v1.PodScheduled && cond.Status == v1.ConditionFalse && cond.Reason == v1.PodReasonUnschedulable {
429 return true, nil
430 }
431 }
432 }
433 if pod.Status.Phase == v1.PodRunning || pod.Status.Phase == v1.PodSucceeded || pod.Status.Phase == v1.PodFailed {
434 return true, fmt.Errorf("Expected pod %q in namespace %q to be in phase Pending, but got phase: %v", podName, namespace, pod.Status.Phase)
435 }
436 return false, nil
437 })
438 }
439
440
441
442 func WaitForPodNameRunningInNamespace(ctx context.Context, c clientset.Interface, podName, namespace string) error {
443 return WaitTimeoutForPodRunningInNamespace(ctx, c, podName, namespace, podStartTimeout)
444 }
445
446
447
448
449 func WaitForPodRunningInNamespaceSlow(ctx context.Context, c clientset.Interface, podName, namespace string) error {
450 return WaitTimeoutForPodRunningInNamespace(ctx, c, podName, namespace, slowPodStartTimeout)
451 }
452
453
454
455
456 func WaitTimeoutForPodRunningInNamespace(ctx context.Context, c clientset.Interface, podName, namespace string, timeout time.Duration) error {
457 return framework.Gomega().Eventually(ctx, framework.RetryNotFound(framework.GetObject(c.CoreV1().Pods(namespace).Get, podName, metav1.GetOptions{}))).
458 WithTimeout(timeout).
459 Should(BeRunningNoRetries())
460 }
461
462
463
464 func WaitForPodRunningInNamespace(ctx context.Context, c clientset.Interface, pod *v1.Pod) error {
465 if pod.Status.Phase == v1.PodRunning {
466 return nil
467 }
468 return WaitTimeoutForPodRunningInNamespace(ctx, c, pod.Name, pod.Namespace, podStartTimeout)
469 }
470
471
472 func WaitTimeoutForPodNoLongerRunningInNamespace(ctx context.Context, c clientset.Interface, podName, namespace string, timeout time.Duration) error {
473 return WaitForPodCondition(ctx, c, namespace, podName, "completed", timeout, func(pod *v1.Pod) (bool, error) {
474 switch pod.Status.Phase {
475 case v1.PodFailed, v1.PodSucceeded:
476 return true, nil
477 }
478 return false, nil
479 })
480 }
481
482
483
484 func WaitForPodNoLongerRunningInNamespace(ctx context.Context, c clientset.Interface, podName, namespace string) error {
485 return WaitTimeoutForPodNoLongerRunningInNamespace(ctx, c, podName, namespace, defaultPodDeletionTimeout)
486 }
487
488
489
490 func WaitTimeoutForPodReadyInNamespace(ctx context.Context, c clientset.Interface, podName, namespace string, timeout time.Duration) error {
491 return WaitForPodCondition(ctx, c, namespace, podName, "running and ready", timeout, func(pod *v1.Pod) (bool, error) {
492 switch pod.Status.Phase {
493 case v1.PodFailed, v1.PodSucceeded:
494 return false, gomega.StopTrying(fmt.Sprintf("The phase of Pod %s is %s which is unexpected.", pod.Name, pod.Status.Phase))
495 case v1.PodRunning:
496 return podutils.IsPodReady(pod), nil
497 }
498 return false, nil
499 })
500 }
501
502
503
504
505 func WaitForPodNotPending(ctx context.Context, c clientset.Interface, ns, podName string) error {
506 return WaitForPodCondition(ctx, c, ns, podName, "not pending", podStartTimeout, func(pod *v1.Pod) (bool, error) {
507 switch pod.Status.Phase {
508 case v1.PodPending:
509 return false, nil
510 default:
511 return true, nil
512 }
513 })
514 }
515
516
517 func WaitForPodSuccessInNamespace(ctx context.Context, c clientset.Interface, podName string, namespace string) error {
518 return WaitForPodSuccessInNamespaceTimeout(ctx, c, podName, namespace, podStartTimeout)
519 }
520
521
522
523
524
525 func WaitForPodNotFoundInNamespace(ctx context.Context, c clientset.Interface, podName, ns string, timeout time.Duration) error {
526 err := framework.Gomega().Eventually(ctx, framework.HandleRetry(func(ctx context.Context) (*v1.Pod, error) {
527 pod, err := c.CoreV1().Pods(ns).Get(ctx, podName, metav1.GetOptions{})
528 if apierrors.IsNotFound(err) {
529 return nil, nil
530 }
531 return pod, err
532 })).WithTimeout(timeout).Should(gomega.BeNil())
533 if err != nil {
534 return fmt.Errorf("expected pod to not be found: %w", err)
535 }
536 return nil
537 }
538
539
540 func WaitForPodsResponding(ctx context.Context, c clientset.Interface, ns string, controllerName string, wantName bool, timeout time.Duration, pods *v1.PodList) error {
541 if timeout == 0 {
542 timeout = podRespondingTimeout
543 }
544 ginkgo.By("trying to dial each unique pod")
545 label := labels.SelectorFromSet(labels.Set(map[string]string{"name": controllerName}))
546 options := metav1.ListOptions{LabelSelector: label.String()}
547
548 type response struct {
549 podName string
550 response string
551 }
552
553 get := func(ctx context.Context) ([]response, error) {
554 currentPods, err := c.CoreV1().Pods(ns).List(ctx, options)
555 if err != nil {
556 return nil, fmt.Errorf("list pods: %w", err)
557 }
558
559 var responses []response
560 for _, pod := range pods.Items {
561
562 if !isElementOf(pod.UID, currentPods) {
563 return nil, gomega.StopTrying(fmt.Sprintf("Pod with UID %s is no longer a member of the replica set. Must have been restarted for some reason.\nCurrent replica set:\n%s", pod.UID, format.Object(currentPods, 1)))
564 }
565
566 ctxUntil, cancel := context.WithTimeout(ctx, singleCallTimeout)
567 defer cancel()
568
569 body, err := c.CoreV1().RESTClient().Get().
570 Namespace(ns).
571 Resource("pods").
572 SubResource("proxy").
573 Name(string(pod.Name)).
574 Do(ctxUntil).
575 Raw()
576
577 if err != nil {
578
579
580
581 return nil, fmt.Errorf("controller %s: failed to Get from replica pod %s:\n%w\nPod status:\n%s",
582 controllerName, pod.Name,
583 err, format.Object(pod.Status, 1))
584 }
585 responses = append(responses, response{podName: pod.Name, response: string(body)})
586 }
587 return responses, nil
588 }
589
590 match := func(responses []response) (func() string, error) {
591
592
593 var unexpected []response
594 for _, response := range responses {
595 if wantName {
596 if response.response != response.podName {
597 unexpected = append(unexpected, response)
598 }
599 } else {
600 if len(response.response) == 0 {
601 unexpected = append(unexpected, response)
602 }
603 }
604 }
605 if len(unexpected) > 0 {
606 return func() string {
607 what := "some response"
608 if wantName {
609 what = "the pod's own name as response"
610 }
611 return fmt.Sprintf("Wanted %s, but the following pods replied with something else:\n%s", what, format.Object(unexpected, 1))
612 }, nil
613 }
614 return nil, nil
615 }
616
617 err := framework.Gomega().
618 Eventually(ctx, framework.HandleRetry(get)).
619 WithTimeout(timeout).
620 Should(framework.MakeMatcher(match))
621 if err != nil {
622 return fmt.Errorf("checking pod responses: %w", err)
623 }
624 return nil
625 }
626
627 func isElementOf(podUID apitypes.UID, pods *v1.PodList) bool {
628 for _, pod := range pods.Items {
629 if pod.UID == podUID {
630 return true
631 }
632 }
633 return false
634 }
635
636
637
638
639 func WaitForNumberOfPods(ctx context.Context, c clientset.Interface, ns string, num int, timeout time.Duration) (pods *v1.PodList, err error) {
640 return WaitForPods(ctx, c, ns, metav1.ListOptions{}, Range{MinMatching: num, MaxMatching: num}, podScheduledBeforeTimeout, "exist", func(pod *v1.Pod) bool {
641 return true
642 })
643 }
644
645
646
647 func WaitForPodsWithLabelScheduled(ctx context.Context, c clientset.Interface, ns string, label labels.Selector) (pods *v1.PodList, err error) {
648 opts := metav1.ListOptions{LabelSelector: label.String()}
649 return WaitForPods(ctx, c, ns, opts, Range{MinFound: 1, AllMatching: true}, podScheduledBeforeTimeout, "be scheduled", func(pod *v1.Pod) bool {
650 return pod.Spec.NodeName != ""
651 })
652 }
653
654
655 func WaitForPodsWithLabel(ctx context.Context, c clientset.Interface, ns string, label labels.Selector) (*v1.PodList, error) {
656 opts := metav1.ListOptions{LabelSelector: label.String()}
657 return WaitForPods(ctx, c, ns, opts, Range{MinFound: 1}, podListTimeout, "exist", func(pod *v1.Pod) bool {
658 return true
659 })
660 }
661
662
663
664 func WaitForPodsWithLabelRunningReady(ctx context.Context, c clientset.Interface, ns string, label labels.Selector, num int, timeout time.Duration) (pods *v1.PodList, err error) {
665 opts := metav1.ListOptions{LabelSelector: label.String()}
666 return WaitForPods(ctx, c, ns, opts, Range{MinFound: num, AllMatching: true}, timeout, "be running and ready", RunningReady)
667 }
668
669
670
671 func WaitForNRestartablePods(ctx context.Context, ps *testutils.PodStore, expect int, timeout time.Duration) ([]string, error) {
672 var pods []*v1.Pod
673
674 get := func(ctx context.Context) ([]*v1.Pod, error) {
675 return ps.List(), nil
676 }
677
678 match := func(allPods []*v1.Pod) (func() string, error) {
679 pods = FilterNonRestartablePods(allPods)
680 if len(pods) != expect {
681 return func() string {
682 return fmt.Sprintf("expected to find non-restartable %d pods, but found %d:\n%s", expect, len(pods), format.Object(pods, 1))
683 }, nil
684 }
685 return nil, nil
686 }
687
688 err := framework.Gomega().
689 Eventually(ctx, framework.HandleRetry(get)).
690 WithTimeout(timeout).
691 Should(framework.MakeMatcher(match))
692 if err != nil {
693 return nil, err
694 }
695
696 podNames := make([]string, len(pods))
697 for i, p := range pods {
698 podNames[i] = p.Name
699 }
700 return podNames, nil
701 }
702
703
704
705
706 func WaitForPodContainerToFail(ctx context.Context, c clientset.Interface, namespace, podName string, containerIndex int, reason string, timeout time.Duration) error {
707 conditionDesc := fmt.Sprintf("container %d failed with reason %s", containerIndex, reason)
708 return WaitForPodCondition(ctx, c, namespace, podName, conditionDesc, timeout, func(pod *v1.Pod) (bool, error) {
709 switch pod.Status.Phase {
710 case v1.PodPending:
711 if len(pod.Status.ContainerStatuses) == 0 {
712 return false, nil
713 }
714 containerStatus := pod.Status.ContainerStatuses[containerIndex]
715 if containerStatus.State.Waiting != nil && containerStatus.State.Waiting.Reason == reason {
716 return true, nil
717 }
718 return false, nil
719 case v1.PodFailed, v1.PodRunning, v1.PodSucceeded:
720 return false, fmt.Errorf("pod was expected to be pending, but it is in the state: %s", pod.Status.Phase)
721 }
722 return false, nil
723 })
724 }
725
726
727 func WaitForPodScheduled(ctx context.Context, c clientset.Interface, namespace, podName string) error {
728 return WaitForPodCondition(ctx, c, namespace, podName, "pod is scheduled", podScheduledBeforeTimeout, func(pod *v1.Pod) (bool, error) {
729 return pod.Spec.NodeName != "", nil
730 })
731 }
732
733
734 func WaitForPodContainerStarted(ctx context.Context, c clientset.Interface, namespace, podName string, containerIndex int, timeout time.Duration) error {
735 conditionDesc := fmt.Sprintf("container %d started", containerIndex)
736 return WaitForPodCondition(ctx, c, namespace, podName, conditionDesc, timeout, func(pod *v1.Pod) (bool, error) {
737 if containerIndex > len(pod.Status.ContainerStatuses)-1 {
738 return false, nil
739 }
740 containerStatus := pod.Status.ContainerStatuses[containerIndex]
741 return *containerStatus.Started, nil
742 })
743 }
744
745
746 func WaitForPodFailedReason(ctx context.Context, c clientset.Interface, pod *v1.Pod, reason string, timeout time.Duration) error {
747 conditionDesc := fmt.Sprintf("failed with reason %s", reason)
748 return WaitForPodCondition(ctx, c, pod.Namespace, pod.Name, conditionDesc, timeout, func(pod *v1.Pod) (bool, error) {
749 switch pod.Status.Phase {
750 case v1.PodSucceeded:
751 return true, errors.New("pod succeeded unexpectedly")
752 case v1.PodFailed:
753 if pod.Status.Reason == reason {
754 return true, nil
755 } else {
756 return true, fmt.Errorf("pod failed with reason %s", pod.Status.Reason)
757 }
758 }
759 return false, nil
760 })
761 }
762
763
764 func WaitForContainerRunning(ctx context.Context, c clientset.Interface, namespace, podName, containerName string, timeout time.Duration) error {
765 conditionDesc := fmt.Sprintf("container %s running", containerName)
766 return WaitForPodCondition(ctx, c, namespace, podName, conditionDesc, timeout, func(pod *v1.Pod) (bool, error) {
767 for _, statuses := range [][]v1.ContainerStatus{pod.Status.ContainerStatuses, pod.Status.InitContainerStatuses, pod.Status.EphemeralContainerStatuses} {
768 for _, cs := range statuses {
769 if cs.Name == containerName {
770 return cs.State.Running != nil, nil
771 }
772 }
773 }
774 return false, nil
775 })
776 }
777
778
779 func WaitForContainerTerminated(ctx context.Context, c clientset.Interface, namespace, podName, containerName string, timeout time.Duration) error {
780 conditionDesc := fmt.Sprintf("container %s terminated", containerName)
781 return WaitForPodCondition(ctx, c, namespace, podName, conditionDesc, timeout, func(pod *v1.Pod) (bool, error) {
782 for _, statuses := range [][]v1.ContainerStatus{pod.Status.ContainerStatuses, pod.Status.InitContainerStatuses, pod.Status.EphemeralContainerStatuses} {
783 for _, cs := range statuses {
784 if cs.Name == containerName {
785 return cs.State.Terminated != nil, nil
786 }
787 }
788 }
789 return false, nil
790 })
791 }
792
View as plain text