Commit b69b63b
authored
test: tolerate connection drops during NGINX reloads in reload-sensitive e2e tests (#10443)
* test(appprotect): tolerate connection drops during reload in watch-namespace-label test
The watch-namespace-label AppProtect e2e test toggles namespace labels, each
of which triggers an NGINX reload plus an App Protect WAF soft reset. Requests
that land during the reload window hit recycled workers and get a closed
connection (access log status 000), surfacing as an uncaught
requests.exceptions.ConnectionError (RemoteDisconnected) that fails the test.
Add a retry_get_until_body_contains helper that retries the request and
tolerates ConnectionError during reloads, mirroring the transient-handling
already done by ensure_response_from_backend and wait_for_reload. Use it at the
three request sites in test_app_protect_watch_namespace_label.py and drop the
now-unused requests import.
Also add FLAKY_RELOAD_REQUESTS.md documenting the pattern so the same fix can be
applied to other reload-sensitive e2e tests.
Test-only change: no product code, codegen, or snapshot updates.
* test: tolerate connection drops during reloads across reload-sensitive e2e tests
Requests that land while NGINX is reloading (worker recycling, or an App Protect
WAF soft reset) hit a closed connection and surface as an uncaught
requests.exceptions.ConnectionError (RemoteDisconnected, access-log status 000),
failing the test on a timing race rather than a real regression.
Apply the pattern documented in FLAKY_RELOAD_REQUESTS.md to the remaining
reload-sensitive tests instead of wrapping whole tests in @pytest.mark.flaky:
Shared helpers (cover the majority of call sites):
- resources_utils: add retry_get_until_status_code (status-based sibling of
retry_get_until_body_contains, supports an SNI/client-cert session and
requests.get kwargs) and retry_get (guards a single request for arbitrary or
negative assertions); both catch ConnectionError while retrying.
- custom_assertions.wait_and_assert_status_code and
ap_resources_utils.send_malicious_request_with_retry now tolerate
ConnectionError, hardening every caller.
Test call sites:
- Replace hand-rolled status-poll loops in test_ingress_mtls.py,
test_ingress_mtls_ingress.py, test_ingress_mtls_mergeable_ingress.py and
test_watch_namespace_label.py with retry_get_until_status_code, preserving
retry budgets and assertions.
- Add except ConnectionError after the load-bearing except SSLError handlers in
the mTLS tests. Note: SSLError subclasses ConnectionError, so SSLError must be
caught first.
- Guard single post-reload requests in test_app_protect_integration.py,
test_app_protect_waf_policies.py, test_app_protect_waf_bundle_source_vs.py,
test_dos.py and test_virtual_server_dos.py with retry_get.
Drop now-unused requests/mock/wait_before_test imports where applicable and
document the new helpers and the SSLError ordering caveat in
FLAKY_RELOAD_REQUESTS.md.
Test-only change: no product code, codegen, or snapshot updates.
* test: fail explicitly when reload-retry helpers never get a response
retry_get_until_body_contains, retry_get_until_status_code and retry_get could
return None when every attempt hit a ConnectionError. Callers immediately access
resp.text/resp.status_code, so a persistent connection drop surfaced as an
AttributeError that obscured the real failure.
Track the last ConnectionError in each helper and pytest.fail with the URL,
attempt count, and last error when no response was ever obtained. When a response
was received but did not match (wrong body/status), still return it so the
caller's own assertion produces its meaningful message.
Test-only change: no product code, codegen, or snapshot updates.
* test: fail explicitly when send_malicious_request_with_retry gets no response
send_malicious_request_with_retry could return None when every attempt hit a
ConnectionError. Callers treat the return value as a real requests.Response, so
a persistent connection drop surfaced as an AttributeError that hid the root
cause.
Track the last ConnectionError and pytest.fail with the URL, attempt count, and
last error when no response was ever obtained (adding the pytest import). When a
response was received but the WAF did not block, still return it so the caller's
own assertion produces its meaningful message.
Test-only change: no product code, codegen, or snapshot updates.
* test: surface root cause when wait_and_assert_status_code gets no response
When every attempt in wait_and_assert_status_code hit a ConnectionError, resp
stayed None and the final assertion reported only "status_code is still not
<code>", hiding the underlying connection drops.
Track the last ConnectionError and pytest.fail with the URL, attempt count, and
last error when no response was ever received; keep the existing status_code
assertion for the connected-but-wrong-status case.
Test-only change: no product code, codegen, or snapshot updates.
* test: avoid trailing sleep and preserve attempt count in reload-retry loops
The reload-tolerant retry loops slept after every attempt, including the final
one, adding needless wall-clock time (~1s per call across 100+ call sites), and
the while-loop rewrites did one fewer request attempt than the original code.
Switch send_malicious_request_with_retry and wait_and_assert_status_code to
range-based loops that restore the original attempt count (initial request plus
up to N retries) and sleep only when another attempt will follow. Apply the same
"no sleep after the last attempt" fix to retry_get_until_body_contains,
retry_get_until_status_code, and retry_get.
Test-only change: no product code, codegen, or snapshot updates.
* test: derive wait_and_assert_status_code failure message from attempt count
The failure/assertion messages hardcoded "After 30 seconds" while the loop now
runs up to 32 attempts (~31s), making failures misleading. Compute
elapsed_seconds from attempts and use it (plus the attempt count) in both the
no-response pytest.fail and the wrong-status assertion, so the messages stay
accurate if the loop bounds change again.
Test-only change: no product code, codegen, or snapshot updates.
* test: tolerate connection drops in ensure_response_from_backend polling loops
ensure_response_from_backend performed unguarded requests.get/session.get calls
in its polling loops, so a connection dropped by an NGINX reload (worker
recycling) raised an uncaught ConnectionError. Many tests call this helper right
after a config change, leaving reload flakiness unaddressed despite the new retry
helpers.
Catch requests.exceptions.ConnectionError in all three loops and keep retrying
within the existing budgets (SSLError stays first since it subclasses
ConnectionError). Seed resp to None and report "no response (connection kept
dropping)" in the failure message when every attempt dropped, so the root cause
is preserved.
Test-only change: no product code, codegen, or snapshot updates.
* test: preserve retry pacing and fix misleading timeout messages
Address review feedback on the reload-tolerant retry work:
- ensure_response_from_backend (default branch): the retry-interval log said
"1 second" and the failure said "after 60 seconds", but the branch sleeps
wait_before_test() (RECONFIGURATION_DELAY=3s) over 30 iterations (~90s).
Base both messages on RECONFIGURATION_DELAY so timeouts are interpretable.
- test_watch_namespace_label.py: import RECONFIGURATION_DELAY and pass
wait_seconds=RECONFIGURATION_DELAY to all six retry_get_until_status_code
calls, restoring the prior ~3s-per-attempt pacing (the default of 1s
shortened the tolerance window after label-change reloads).
Test-only change: no product code, codegen, or snapshot updates.1 parent 3ceb3c6 commit b69b63b
14 files changed
Lines changed: 523 additions & 250 deletions
File tree
- tests/suite
- utils
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | | - | |
3 | 2 | | |
4 | 3 | | |
5 | 4 | | |
| |||
28 | 27 | | |
29 | 28 | | |
30 | 29 | | |
| 30 | + | |
31 | 31 | | |
32 | 32 | | |
33 | 33 | | |
| |||
209 | 209 | | |
210 | 210 | | |
211 | 211 | | |
212 | | - | |
| 212 | + | |
213 | 213 | | |
214 | 214 | | |
215 | 215 | | |
| |||
233 | 233 | | |
234 | 234 | | |
235 | 235 | | |
236 | | - | |
| 236 | + | |
237 | 237 | | |
238 | 238 | | |
239 | 239 | | |
| |||
260 | 260 | | |
261 | 261 | | |
262 | 262 | | |
263 | | - | |
| 263 | + | |
264 | 264 | | |
265 | 265 | | |
266 | 266 | | |
| |||
288 | 288 | | |
289 | 289 | | |
290 | 290 | | |
291 | | - | |
| 291 | + | |
292 | 292 | | |
293 | 293 | | |
294 | 294 | | |
| |||
366 | 366 | | |
367 | 367 | | |
368 | 368 | | |
369 | | - | |
| 369 | + | |
370 | 370 | | |
371 | 371 | | |
372 | 372 | | |
| |||
434 | 434 | | |
435 | 435 | | |
436 | 436 | | |
437 | | - | |
| 437 | + | |
438 | 438 | | |
439 | | - | |
| 439 | + | |
440 | 440 | | |
441 | 441 | | |
442 | 442 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | | - | |
3 | 2 | | |
4 | 3 | | |
5 | 4 | | |
| |||
14 | 13 | | |
15 | 14 | | |
16 | 15 | | |
17 | | - | |
| 16 | + | |
18 | 17 | | |
19 | 18 | | |
20 | 19 | | |
| |||
208 | 207 | | |
209 | 208 | | |
210 | 209 | | |
211 | | - | |
| 210 | + | |
| 211 | + | |
212 | 212 | | |
213 | | - | |
| 213 | + | |
214 | 214 | | |
215 | 215 | | |
216 | 216 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | | - | |
3 | 2 | | |
4 | 3 | | |
5 | 4 | | |
| |||
17 | 16 | | |
18 | 17 | | |
19 | 18 | | |
| 19 | + | |
20 | 20 | | |
21 | 21 | | |
22 | 22 | | |
| |||
197 | 197 | | |
198 | 198 | | |
199 | 199 | | |
200 | | - | |
| 200 | + | |
201 | 201 | | |
202 | | - | |
| 202 | + | |
203 | 203 | | |
204 | 204 | | |
205 | 205 | | |
206 | 206 | | |
207 | | - | |
| 207 | + | |
208 | 208 | | |
209 | | - | |
| 209 | + | |
210 | 210 | | |
211 | 211 | | |
212 | 212 | | |
| |||
268 | 268 | | |
269 | 269 | | |
270 | 270 | | |
271 | | - | |
| 271 | + | |
272 | 272 | | |
273 | | - | |
| 273 | + | |
274 | 274 | | |
275 | 275 | | |
276 | 276 | | |
277 | 277 | | |
278 | | - | |
| 278 | + | |
279 | 279 | | |
280 | | - | |
| 280 | + | |
281 | 281 | | |
282 | 282 | | |
283 | 283 | | |
| |||
333 | 333 | | |
334 | 334 | | |
335 | 335 | | |
336 | | - | |
| 336 | + | |
337 | 337 | | |
338 | | - | |
| 338 | + | |
339 | 339 | | |
340 | 340 | | |
341 | 341 | | |
| |||
449 | 449 | | |
450 | 450 | | |
451 | 451 | | |
452 | | - | |
| 452 | + | |
453 | 453 | | |
454 | | - | |
| 454 | + | |
455 | 455 | | |
456 | 456 | | |
457 | 457 | | |
| |||
546 | 546 | | |
547 | 547 | | |
548 | 548 | | |
549 | | - | |
| 549 | + | |
550 | 550 | | |
551 | | - | |
| 551 | + | |
552 | 552 | | |
553 | 553 | | |
554 | 554 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
4 | | - | |
5 | 4 | | |
6 | 5 | | |
7 | 6 | | |
| |||
20 | 19 | | |
21 | 20 | | |
22 | 21 | | |
| 22 | + | |
23 | 23 | | |
24 | 24 | | |
25 | 25 | | |
| |||
156 | 156 | | |
157 | 157 | | |
158 | 158 | | |
159 | | - | |
160 | | - | |
| 159 | + | |
| 160 | + | |
161 | 161 | | |
162 | 162 | | |
163 | 163 | | |
| |||
176 | 176 | | |
177 | 177 | | |
178 | 178 | | |
179 | | - | |
180 | | - | |
| 179 | + | |
| 180 | + | |
181 | 181 | | |
182 | | - | |
183 | | - | |
184 | | - | |
185 | | - | |
186 | | - | |
187 | | - | |
188 | | - | |
189 | | - | |
190 | 182 | | |
191 | 183 | | |
192 | 184 | | |
| |||
202 | 194 | | |
203 | 195 | | |
204 | 196 | | |
205 | | - | |
206 | | - | |
| 197 | + | |
| 198 | + | |
207 | 199 | | |
208 | | - | |
209 | | - | |
210 | | - | |
211 | | - | |
212 | | - | |
213 | | - | |
214 | | - | |
215 | | - | |
216 | 200 | | |
217 | 201 | | |
218 | 202 | | |
0 commit comments