@@ -21,12 +21,14 @@ import (
21
21
"fmt"
22
22
"time"
23
23
24
+ "github.com/onsi/gomega"
24
25
batchv1 "k8s.io/api/batch/v1"
25
26
v1 "k8s.io/api/core/v1"
26
27
apierrors "k8s.io/apimachinery/pkg/api/errors"
27
28
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28
29
"k8s.io/apimachinery/pkg/util/wait"
29
30
clientset "k8s.io/client-go/kubernetes"
31
+ "k8s.io/klog/v2"
30
32
"k8s.io/kubernetes/test/e2e/framework"
31
33
"k8s.io/kubernetes/test/utils/format"
32
34
"k8s.io/utils/ptr"
@@ -82,14 +84,33 @@ func waitForJobPodsInPhase(ctx context.Context, c clientset.Interface, ns, jobNa
82
84
// both conformance CI jobs with GA-only features and e2e CI jobs with all default-enabled features.
83
85
// So, we need to skip "Complete" condition reason verifications in the e2e conformance test cases.
84
86
func WaitForJobComplete (ctx context.Context , c clientset.Interface , ns , jobName string , reason * string , completions int32 ) error {
85
- if err := wait .PollUntilContextTimeout (ctx , framework .Poll , JobTimeout , false , func (ctx context.Context ) (bool , error ) {
86
- curr , err := c .BatchV1 ().Jobs (ns ).Get (ctx , jobName , metav1.GetOptions {})
87
+ // This function is called by HandleRetry, which will retry
88
+ // on transient API errors or stop polling in the case of other errors.
89
+ get := func (ctx context.Context ) (* batchv1.Job , error ) {
90
+ job , err := c .BatchV1 ().Jobs (ns ).Get (ctx , jobName , metav1.GetOptions {})
87
91
if err != nil {
88
- return false , err
92
+ return nil , err
93
+ }
94
+ if isJobFailed (job ) {
95
+ return nil , gomega .StopTrying ("job failed while waiting for its completion" ).Attach ("job" , job )
96
+ }
97
+ return job , nil
98
+ }
99
+ match := func (job * batchv1.Job ) (func () string , error ) {
100
+ if job .Status .Succeeded == completions {
101
+ return nil , nil
89
102
}
90
- return curr .Status .Succeeded == completions , nil
91
- }); err != nil {
92
- return nil
103
+ return func () string {
104
+ return fmt .Sprintf ("expected job %q to have %v successful pods. got %v" , klog .KObj (job ), completions , job .Status .Succeeded )
105
+ }, nil
106
+ }
107
+ err := framework .Gomega ().
108
+ Eventually (ctx , framework .HandleRetry (get )).
109
+ WithTimeout (JobTimeout ).
110
+ WithPolling (framework .Poll ).
111
+ Should (framework .MakeMatcher (match ))
112
+ if err != nil {
113
+ return err
93
114
}
94
115
return WaitForJobCondition (ctx , c , ns , jobName , batchv1 .JobComplete , reason )
95
116
}
@@ -117,48 +138,55 @@ func WaitForJobSuspend(ctx context.Context, c clientset.Interface, ns, jobName s
117
138
}
118
139
119
140
// WaitForJobFailed uses c to wait for the Job jobName in namespace ns to fail
120
- func WaitForJobFailed (c clientset.Interface , ns , jobName string ) error {
121
- return wait .PollImmediate (framework .Poll , JobTimeout , func () (bool , error ) {
122
- curr , err := c .BatchV1 ().Jobs (ns ).Get (context .TODO (), jobName , metav1.GetOptions {})
141
+ func WaitForJobFailed (ctx context.Context , c clientset.Interface , ns , jobName string ) error {
142
+ // This function is called by HandleRetry, which will retry
143
+ // on transient API errors or stop polling in the case of other errors.
144
+ get := func (ctx context.Context ) (* batchv1.Job , error ) {
145
+ job , err := c .BatchV1 ().Jobs (ns ).Get (ctx , jobName , metav1.GetOptions {})
123
146
if err != nil {
124
- return false , err
147
+ return nil , err
125
148
}
126
-
127
- return isJobFailed (curr ), nil
128
- })
149
+ if isJobCompleted (job ) {
150
+ return nil , gomega .StopTrying ("job completed while waiting for its failure" ).Attach ("job" , job )
151
+ }
152
+ return job , nil
153
+ }
154
+ match := func (job * batchv1.Job ) (func () string , error ) {
155
+ if isJobFailed (job ) {
156
+ return nil , nil
157
+ }
158
+ return func () string {
159
+ return fmt .Sprintf ("expected job %q to fail" , klog .KObj (job ))
160
+ }, nil
161
+ }
162
+ return framework .Gomega ().
163
+ Eventually (ctx , framework .HandleRetry (get )).
164
+ WithTimeout (JobTimeout ).
165
+ WithPolling (framework .Poll ).
166
+ Should (framework .MakeMatcher (match ))
129
167
}
130
168
131
169
// WaitForJobCondition waits for the specified Job to have the expected condition with the specific reason.
132
170
// When the nil reason is passed, the "reason" string in the condition is
133
171
// not checked.
134
172
func WaitForJobCondition (ctx context.Context , c clientset.Interface , ns , jobName string , cType batchv1.JobConditionType , reason * string ) error {
135
- err := wait .PollUntilContextTimeout (ctx , framework .Poll , JobTimeout , false , func (ctx context.Context ) (bool , error ) {
136
- curr , err := c .BatchV1 ().Jobs (ns ).Get (ctx , jobName , metav1.GetOptions {})
137
- if err != nil {
138
- return false , err
139
- }
140
- for _ , c := range curr .Status .Conditions {
173
+ match := func (job * batchv1.Job ) (func () string , error ) {
174
+ for _ , c := range job .Status .Conditions {
141
175
if c .Type == cType && c .Status == v1 .ConditionTrue {
142
176
if reason == nil || * reason == c .Reason {
143
- return true , nil
177
+ return nil , nil
144
178
}
145
179
}
146
180
}
147
- return false , nil
148
- })
149
- if err != nil {
150
- return fmt .Errorf ("waiting for Job %q to have the condition %q with reason: %v: %w" , jobName , cType , reason , err )
151
- }
152
- return nil
153
- }
154
-
155
- func isJobFailed (j * batchv1.Job ) bool {
156
- for _ , c := range j .Status .Conditions {
157
- if (c .Type == batchv1 .JobFailed ) && c .Status == v1 .ConditionTrue {
158
- return true
159
- }
181
+ return func () string {
182
+ return fmt .Sprintf ("expected job %q to reach the expected condition %q with reason %q" , klog .KObj (job ), cType , ptr .Deref (reason , "<nil>" ))
183
+ }, nil
160
184
}
161
- return false
185
+ return framework .Gomega ().
186
+ Eventually (ctx , framework .GetObject (c .BatchV1 ().Jobs (ns ).Get , jobName , metav1.GetOptions {})).
187
+ WithTimeout (JobTimeout ).
188
+ WithPolling (framework .Poll ).
189
+ Should (framework .MakeMatcher (match ))
162
190
}
163
191
164
192
// WaitForJobFinish uses c to wait for the Job jobName in namespace ns to finish (either Failed or Complete).
@@ -179,8 +207,20 @@ func WaitForJobFinishWithTimeout(ctx context.Context, c clientset.Interface, ns,
179
207
}
180
208
181
209
func isJobFinished (j * batchv1.Job ) bool {
210
+ return isJobCompleted (j ) || isJobFailed (j )
211
+ }
212
+
213
+ func isJobFailed (j * batchv1.Job ) bool {
214
+ return isConditionTrue (j , batchv1 .JobFailed )
215
+ }
216
+
217
+ func isJobCompleted (j * batchv1.Job ) bool {
218
+ return isConditionTrue (j , batchv1 .JobComplete )
219
+ }
220
+
221
+ func isConditionTrue (j * batchv1.Job , condition batchv1.JobConditionType ) bool {
182
222
for _ , c := range j .Status .Conditions {
183
- if ( c .Type == batchv1 . JobComplete || c . Type == batchv1 . JobFailed ) && c .Status == v1 .ConditionTrue {
223
+ if c .Type == condition && c .Status == v1 .ConditionTrue {
184
224
return true
185
225
}
186
226
}
0 commit comments