k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/test/e2e/framework/job/wait.go (about) 1 /* 2 Copyright 2019 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package job 18 19 import ( 20 "context" 21 "fmt" 22 "time" 23 24 batchv1 "k8s.io/api/batch/v1" 25 v1 "k8s.io/api/core/v1" 26 apierrors "k8s.io/apimachinery/pkg/api/errors" 27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 "k8s.io/apimachinery/pkg/util/wait" 29 clientset "k8s.io/client-go/kubernetes" 30 "k8s.io/kubernetes/test/e2e/framework" 31 "k8s.io/kubernetes/test/utils/format" 32 "k8s.io/utils/ptr" 33 ) 34 35 // JobState is used to verify if Job matches a particular condition. 36 // If it matches, an empty string is returned. 37 // Otherwise, the string explains why the condition is not matched. 38 // This should be a short string. A dump of the job object will 39 // get added by the caller. 40 type JobState func(job *batchv1.Job) string 41 42 // WaitForJobPodsRunning wait for all pods for the Job named JobName in namespace ns to become Running. Only use 43 // when pods will run for a long time, or it will be racy. 44 func WaitForJobPodsRunning(ctx context.Context, c clientset.Interface, ns, jobName string, expectedCount int32) error { 45 return waitForJobPodsInPhase(ctx, c, ns, jobName, expectedCount, v1.PodRunning) 46 } 47 48 // WaitForJobPodsSucceeded wait for all pods for the Job named JobName in namespace ns to become Succeeded. 49 func WaitForJobPodsSucceeded(ctx context.Context, c clientset.Interface, ns, jobName string, expectedCount int32) error { 50 return waitForJobPodsInPhase(ctx, c, ns, jobName, expectedCount, v1.PodSucceeded) 51 } 52 53 // waitForJobPodsInPhase wait for all pods for the Job named JobName in namespace ns to be in a given phase. 54 func waitForJobPodsInPhase(ctx context.Context, c clientset.Interface, ns, jobName string, expectedCount int32, phase v1.PodPhase) error { 55 return wait.PollWithContext(ctx, framework.Poll, JobTimeout, func(ctx context.Context) (bool, error) { 56 pods, err := GetJobPods(ctx, c, ns, jobName) 57 if err != nil { 58 return false, err 59 } 60 count := int32(0) 61 for _, p := range pods.Items { 62 if p.Status.Phase == phase { 63 count++ 64 } 65 } 66 return count == expectedCount, nil 67 }) 68 } 69 70 // WaitForJobComplete uses c to wait for completions to complete for the Job jobName in namespace ns. 71 func WaitForJobComplete(ctx context.Context, c clientset.Interface, ns, jobName string, completions int32) error { 72 return wait.PollWithContext(ctx, framework.Poll, JobTimeout, func(ctx context.Context) (bool, error) { 73 curr, err := c.BatchV1().Jobs(ns).Get(ctx, jobName, metav1.GetOptions{}) 74 if err != nil { 75 return false, err 76 } 77 return curr.Status.Succeeded == completions, nil 78 }) 79 } 80 81 // WaitForJobReady waits for particular value of the Job .status.ready field 82 func WaitForJobReady(ctx context.Context, c clientset.Interface, ns, jobName string, ready *int32) error { 83 return WaitForJobState(ctx, c, ns, jobName, JobTimeout, func(job *batchv1.Job) string { 84 if ptr.Equal(ready, job.Status.Ready) { 85 return "" 86 } 87 return "job does not match intended ready status" 88 }) 89 } 90 91 // WaitForJobSuspend uses c to wait for suspend condition for the Job jobName in namespace ns. 92 func WaitForJobSuspend(ctx context.Context, c clientset.Interface, ns, jobName string) error { 93 return WaitForJobState(ctx, c, ns, jobName, JobTimeout, func(job *batchv1.Job) string { 94 for _, c := range job.Status.Conditions { 95 if c.Type == batchv1.JobSuspended && c.Status == v1.ConditionTrue { 96 return "" 97 } 98 } 99 return "job should be suspended" 100 }) 101 } 102 103 // WaitForJobFailed uses c to wait for the Job jobName in namespace ns to fail 104 func WaitForJobFailed(c clientset.Interface, ns, jobName string) error { 105 return wait.PollImmediate(framework.Poll, JobTimeout, func() (bool, error) { 106 curr, err := c.BatchV1().Jobs(ns).Get(context.TODO(), jobName, metav1.GetOptions{}) 107 if err != nil { 108 return false, err 109 } 110 111 return isJobFailed(curr), nil 112 }) 113 } 114 115 func isJobFailed(j *batchv1.Job) bool { 116 for _, c := range j.Status.Conditions { 117 if (c.Type == batchv1.JobFailed) && c.Status == v1.ConditionTrue { 118 return true 119 } 120 } 121 return false 122 } 123 124 // WaitForJobFinish uses c to wait for the Job jobName in namespace ns to finish (either Failed or Complete). 125 func WaitForJobFinish(ctx context.Context, c clientset.Interface, ns, jobName string) error { 126 return wait.PollUntilContextTimeout(ctx, framework.Poll, JobTimeout, true, func(ctx context.Context) (bool, error) { 127 curr, err := c.BatchV1().Jobs(ns).Get(ctx, jobName, metav1.GetOptions{}) 128 if err != nil { 129 return false, err 130 } 131 132 return isJobFinished(curr), nil 133 }) 134 } 135 136 func isJobFinished(j *batchv1.Job) bool { 137 for _, c := range j.Status.Conditions { 138 if (c.Type == batchv1.JobComplete || c.Type == batchv1.JobFailed) && c.Status == v1.ConditionTrue { 139 return true 140 } 141 } 142 143 return false 144 } 145 146 // WaitForJobGone uses c to wait for up to timeout for the Job named jobName in namespace ns to be removed. 147 func WaitForJobGone(ctx context.Context, c clientset.Interface, ns, jobName string, timeout time.Duration) error { 148 return wait.PollWithContext(ctx, framework.Poll, timeout, func(ctx context.Context) (bool, error) { 149 _, err := c.BatchV1().Jobs(ns).Get(ctx, jobName, metav1.GetOptions{}) 150 if apierrors.IsNotFound(err) { 151 return true, nil 152 } 153 return false, err 154 }) 155 } 156 157 // WaitForAllJobPodsGone waits for all pods for the Job named jobName in namespace ns 158 // to be deleted. 159 func WaitForAllJobPodsGone(ctx context.Context, c clientset.Interface, ns, jobName string) error { 160 return wait.PollUntilContextTimeout(ctx, framework.Poll, JobTimeout, true, func(ctx context.Context) (bool, error) { 161 pods, err := GetJobPods(ctx, c, ns, jobName) 162 if err != nil { 163 return false, err 164 } 165 return len(pods.Items) == 0, nil 166 }) 167 } 168 169 // WaitForJobState waits for a job to be matched to the given condition. 170 // The condition callback may use gomega.StopTrying to abort early. 171 func WaitForJobState(ctx context.Context, c clientset.Interface, ns, jobName string, timeout time.Duration, state JobState) error { 172 return framework.Gomega(). 173 Eventually(ctx, framework.RetryNotFound(framework.GetObject(c.BatchV1().Jobs(ns).Get, jobName, metav1.GetOptions{}))). 174 WithTimeout(timeout). 175 Should(framework.MakeMatcher(func(job *batchv1.Job) (func() string, error) { 176 matches := state(job) 177 if matches == "" { 178 return nil, nil 179 } 180 return func() string { 181 return fmt.Sprintf("%v\n%s", matches, format.Object(job, 1)) 182 }, nil 183 })) 184 }