Skip to content

Commit b69b63b

Browse files
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
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# Fixing flaky e2e tests: connection drops during NGINX reloads
2+
3+
## Symptom
4+
5+
A test intermittently fails with:
6+
7+
requests.exceptions.ConnectionError:
8+
('Connection aborted.', RemoteDisconnected('Remote end closed connection without response'))
9+
10+
Often accompanied in the IC logs by an access-log line with status `000`, and
11+
around a config change (Ingress/Policy/namespace-label update) that triggers an
12+
NGINX reload. With App Protect, the WAF "soft reset" recycles workers and can
13+
take several seconds, widening the window where in-flight connections are closed.
14+
15+
## Root cause
16+
17+
The test sends an HTTP request that lands exactly while NGINX is reloading and
18+
recycling worker processes. NGINX closes the connection, `requests` raises an
19+
uncaught `ConnectionError`, and the test crashes instead of retrying. This is a
20+
timing race, not a product bug -- a dropped connection during a reload is
21+
expected and should be retried (the same way `ensure_response_from_backend` and
22+
`wait_for_reload` already tolerate transients).
23+
24+
## When this pattern applies
25+
26+
Apply the fix to any e2e test that BOTH:
27+
28+
1. Mutates config that causes a reload (apply/patch/delete of Ingress, VS/VSR,
29+
TransportServer, Policy, ConfigMap, or namespace label/watch changes), AND
30+
2. Sends traffic (`requests.get/post/...`) shortly after, while a reload may be
31+
in progress -- especially inside a body/status polling loop.
32+
33+
Do NOT apply it to tests that only send traffic against already-stable config
34+
and never reload mid-traffic (e.g. `test_app_protect_watch_namespace.py`).
35+
36+
## The fix
37+
38+
Do not wrap the whole test in `@pytest.mark.flaky` -- that reruns the entire
39+
expensive test and can mask real regressions. Instead, make the specific request
40+
resilient.
41+
42+
Use one of the shared helpers in `suite/utils/resources_utils.py`, all of which
43+
catch `requests.exceptions.ConnectionError` while retrying:
44+
45+
- `retry_get_until_body_contains(req_url, host, expected_body)` -- poll until a
46+
substring appears in the body.
47+
- `retry_get_until_status_code(req_url, host, expected_status, session=..., **kwargs)`
48+
-- poll until a status code matches; supports an SNI/client-cert `session` and
49+
extra `requests.get` kwargs (`cert`, `verify`, `allow_redirects`, ...).
50+
- `retry_get(req_url, host, **kwargs)` -- guard a single request whose assertion
51+
cannot be expressed as "body contains X" or "status == Y" (e.g. asserting a
52+
substring is absent, or an arbitrary status); retries only on `ConnectionError`.
53+
54+
The shared helpers `wait_and_assert_status_code` (`suite/utils/custom_assertions.py`)
55+
and `send_malicious_request_with_retry` (`suite/utils/ap_resources_utils.py`) also
56+
tolerate `ConnectionError`, so their many callers are covered automatically.
57+
58+
```python
59+
from suite.utils.resources_utils import retry_get_until_body_contains
60+
61+
resp = retry_get_until_body_contains(req_url, ingress_host, expected_body)
62+
assert expected_body in resp.text
63+
assert resp.status_code == 200
64+
```
65+
66+
Replace hand-rolled loops like:
67+
68+
```python
69+
resp = requests.get(url, headers={"host": host}, verify=False)
70+
retry = 0
71+
while expected not in resp.text and retry <= 60:
72+
resp = requests.get(url, headers={"host": host}, verify=False) # can raise ConnectionError
73+
retry += 1
74+
wait_before_test(1)
75+
```
76+
77+
with a single `retry_get_until_body_contains(...)` call.
78+
79+
If a test needs a method/headers/body the helper doesn't cover, replicate its
80+
core guarantee: catch `requests.exceptions.ConnectionError` inside the retry loop
81+
and continue, rather than letting it propagate.
82+
83+
Note: `requests.exceptions.SSLError` is a *subclass* of `ConnectionError`. When a
84+
test relies on catching `SSLError` (e.g. mTLS client-cert tests), keep the
85+
`except SSLError` handler **before** any `except ConnectionError` handler so the
86+
SSL case is not swallowed.
87+
88+
## Reference implementation
89+
90+
See `test_app_protect_watch_namespace_label.py` and the
91+
`retry_get_until_body_contains` helper in `suite/utils/resources_utils.py`.
92+
For status-based and single-request variants, see `retry_get_until_status_code`
93+
and `retry_get` in the same file, and the mTLS tests (`test_ingress_mtls*.py`)
94+
for the SSLError-ordering pattern.
95+
96+
## Checklist for the AI applying this
97+
98+
- [ ] Confirm the test reloads config AND sends traffic that could overlap it.
99+
- [ ] Replace unguarded `requests.*` retry loops with `retry_get_until_body_contains`
100+
(or add `except requests.exceptions.ConnectionError: continue` to the loop).
101+
- [ ] Guard single pre-loop requests that immediately follow a config change too.
102+
- [ ] Preserve existing assertions and the ~60s retry budget.
103+
- [ ] Remove the now-unused `import requests` if no other usage remains in the file.
104+
- [ ] Do NOT add `@pytest.mark.flaky`.
105+
- [ ] Test-only change: no product code, codegen, or snapshot updates.

tests/suite/test_app_protect_integration.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import pytest
2-
import requests
32
import yaml
43
from settings import CRDS, TEST_DATA
54
from suite.utils.ap_resources_utils import (
@@ -28,6 +27,7 @@
2827
get_pod_name_that_contains,
2928
get_pods_amount,
3029
get_test_file_name,
30+
retry_get,
3131
scale_deployment,
3232
wait_before_test,
3333
wait_until_all_pods_are_ready,
@@ -209,7 +209,7 @@ def test_ap_enable_true_policy_correct(
209209
ensure_response_from_backend(appprotect_setup.req_url, ingress_host, check404=True)
210210

211211
print("----------------------- Send request ----------------------")
212-
response = requests.get(appprotect_setup.req_url + "/<script>", headers={"host": ingress_host}, verify=False)
212+
response = retry_get(appprotect_setup.req_url + "/<script>", ingress_host, verify=False)
213213
print(response.text)
214214
delete_items_from_yaml(kube_apis, src_ing_yaml, test_namespace)
215215
assert_invalid_responses(response)
@@ -233,7 +233,7 @@ def test_ap_enable_false_policy_correct(
233233
wait_before_test(5)
234234

235235
print("----------------------- Send request ----------------------")
236-
response = requests.get(appprotect_setup.req_url + "/<script>", headers={"host": ingress_host}, verify=False)
236+
response = retry_get(appprotect_setup.req_url + "/<script>", ingress_host, verify=False)
237237
print(response.text)
238238
delete_items_from_yaml(kube_apis, src_ing_yaml, test_namespace)
239239
assert_valid_responses(response)
@@ -260,7 +260,7 @@ def test_ap_enable_true_policy_incorrect(
260260
ensure_response_from_backend(appprotect_setup.req_url, ingress_host, check404=True)
261261

262262
print("----------------------- Send request ----------------------")
263-
response = requests.get(appprotect_setup.req_url + "/<script>", headers={"host": ingress_host}, verify=False)
263+
response = retry_get(appprotect_setup.req_url + "/<script>", ingress_host, verify=False)
264264
print(response.text)
265265
delete_items_from_yaml(kube_apis, src_ing_yaml, test_namespace)
266266
assert_invalid_responses(response)
@@ -288,7 +288,7 @@ def test_ap_enable_false_policy_incorrect(
288288

289289
print("----------------------- Send request ----------------------")
290290
wait_before_test(5)
291-
response = requests.get(appprotect_setup.req_url + "/<script>", headers={"host": ingress_host}, verify=False)
291+
response = retry_get(appprotect_setup.req_url + "/<script>", ingress_host, verify=False)
292292
print(response.text)
293293
delete_items_from_yaml(kube_apis, src_ing_yaml, test_namespace)
294294
assert_valid_responses(response)
@@ -366,7 +366,7 @@ def test_ap_multi_sec_logs(
366366
ensure_response_from_backend(appprotect_setup.req_url, ingress_host, check404=True)
367367

368368
print("----------------------- Send request ----------------------")
369-
response = requests.get(appprotect_setup.req_url + "/<script>", headers={"host": ingress_host}, verify=False)
369+
response = retry_get(appprotect_setup.req_url + "/<script>", ingress_host, verify=False)
370370
print(response.text)
371371
syslog_pod = get_pod_name_that_contains(kube_apis.v1, test_namespace, "syslog-")
372372
syslog2_pod = get_pod_name_that_contains(kube_apis.v1, test_namespace, "syslog2")
@@ -434,9 +434,9 @@ def test_ap_enable_true_policy_correct_uds(
434434
wait_before_test(120)
435435
ensure_response_from_backend(appprotect_setup.req_url, ingress_host, check404=True)
436436
print("----------------------- Send request ----------------------")
437-
response1 = requests.get(appprotect_setup.req_url, headers={"host": ingress_host}, verify=False, data="kic")
437+
response1 = retry_get(appprotect_setup.req_url, ingress_host, verify=False, data="kic")
438438
print(response1.text)
439-
response2 = requests.get(appprotect_setup.req_url + "/<script>", headers={"host": ingress_host}, verify=False)
439+
response2 = retry_get(appprotect_setup.req_url + "/<script>", ingress_host, verify=False)
440440
print(response2.text)
441441
reload_ms = get_last_reload_time(appprotect_setup.metrics_url, "nginx")
442442
print(f"last reload duration: {reload_ms} ms")

tests/suite/test_app_protect_waf_bundle_source_vs.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import pytest
2-
import requests
32
from settings import TEST_DATA
43
from suite.utils.ap_resources_utils import (
54
assert_waf_blocked,
@@ -14,7 +13,7 @@
1413
)
1514
from suite.utils.custom_resources_utils import read_custom_resource, wait_for_resource_status
1615
from suite.utils.policy_resources_utils import delete_policy
17-
from suite.utils.resources_utils import wait_before_test
16+
from suite.utils.resources_utils import retry_get, wait_before_test
1817
from suite.utils.vs_vsr_resources_utils import (
1918
create_virtual_server_from_yaml,
2019
delete_virtual_server,
@@ -208,9 +207,10 @@ def test_bundle_source_invalid_url(
208207
# WAF is inactive so a malicious request should NOT be blocked with
209208
# the rejection page. The VS may return a 500 or the backend's response
210209
# depending on how the NGINX config was generated.
211-
response = requests.get(
210+
# Tolerate a connection dropped by an NGINX reload; retry the single request.
211+
response = retry_get(
212212
virtual_server_setup.backend_1_url + "</script>",
213-
headers={"host": virtual_server_setup.vs_host},
213+
virtual_server_setup.vs_host,
214214
)
215215
assert "The requested URL was rejected" not in response.text
216216

tests/suite/test_app_protect_waf_policies.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import pytest
2-
import requests
32
from settings import TEST_DATA
43
from suite.utils.ap_resources_utils import (
54
create_ap_logconf_from_yaml,
@@ -17,6 +16,7 @@
1716
create_items_from_yaml,
1817
get_file_contents,
1918
get_pod_name_that_contains,
19+
retry_get,
2020
wait_before_test,
2121
)
2222
from suite.utils.vs_vsr_resources_utils import (
@@ -197,16 +197,16 @@ def test_ap_waf_policy_block(
197197
wait_before_test(120)
198198

199199
print("----------------------- Send request with embedded malicious script----------------------")
200-
response1 = requests.get(
200+
response1 = retry_get(
201201
virtual_server_setup.backend_1_url + "</script>",
202-
headers={"host": virtual_server_setup.vs_host},
202+
virtual_server_setup.vs_host,
203203
)
204204
print(response1.text)
205205

206206
print("----------------------- Send request with blocked keyword in UDS----------------------")
207-
response2 = requests.get(
207+
response2 = retry_get(
208208
virtual_server_setup.backend_1_url,
209-
headers={"host": virtual_server_setup.vs_host},
209+
virtual_server_setup.vs_host,
210210
data="kic",
211211
)
212212
print(response2.text)
@@ -268,16 +268,16 @@ def test_ap_waf_policy_allow(
268268
wait_before_test(120)
269269

270270
print("----------------------- Send request with embedded malicious script----------------------")
271-
response1 = requests.get(
271+
response1 = retry_get(
272272
virtual_server_setup.backend_1_url + "</script>",
273-
headers={"host": virtual_server_setup.vs_host},
273+
virtual_server_setup.vs_host,
274274
)
275275
print(response1.text)
276276

277277
print("----------------------- Send request with blocked keyword in UDS----------------------")
278-
response2 = requests.get(
278+
response2 = retry_get(
279279
virtual_server_setup.backend_1_url,
280-
headers={"host": virtual_server_setup.vs_host},
280+
virtual_server_setup.vs_host,
281281
data="kic",
282282
)
283283
print(response2.text)
@@ -333,9 +333,9 @@ def test_ap_waf_policy_multi_logs(
333333
wait_before_test(120)
334334

335335
print("----------------------- Send request with embedded malicious script----------------------")
336-
response = requests.get(
336+
response = retry_get(
337337
virtual_server_setup.backend_1_url + "</script>",
338-
headers={"host": virtual_server_setup.vs_host},
338+
virtual_server_setup.vs_host,
339339
)
340340
print(response.text)
341341
syslog_pod = get_pod_name_that_contains(kube_apis.v1, test_namespace, "syslog")
@@ -449,9 +449,9 @@ def test_ap_waf_policy_block(
449449
ap_crd_info = read_ap_custom_resource(kube_apis.custom_objects, test_namespace, "appolicies", ap_policy_uds)
450450
assert_ap_crd_info(ap_crd_info, ap_policy_uds)
451451
wait_before_test(120)
452-
response = requests.get(
452+
response = retry_get(
453453
f'{req_url}{v_s_route_setup.route_m.paths[0]}+"</script>"',
454-
headers={"host": v_s_route_setup.vs_host},
454+
v_s_route_setup.vs_host,
455455
)
456456
print(response.text)
457457
delete_policy(kube_apis.custom_objects, "waf-policy", v_s_route_setup.route_m.namespace)
@@ -546,9 +546,9 @@ def test_ap_waf_policy_block(
546546
ap_crd_info = read_ap_custom_resource(kube_apis.custom_objects, test_namespace, "appolicies", ap_policy_uds)
547547
assert_ap_crd_info(ap_crd_info, ap_policy_uds)
548548
wait_before_test(120)
549-
response = requests.get(
549+
response = retry_get(
550550
f'{req_url}{v_s_route_selector_setup.route_m.paths[0]}+"</script>"',
551-
headers={"host": v_s_route_selector_setup.vs_host},
551+
v_s_route_selector_setup.vs_host,
552552
)
553553
print(response.text)
554554
delete_policy(kube_apis.custom_objects, "waf-policy", v_s_route_selector_setup.route_m.namespace)

tests/suite/test_app_protect_watch_namespace_label.py

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import time
22

33
import pytest
4-
import requests
54
from settings import TEST_DATA
65
from suite.utils.ap_resources_utils import (
76
create_ap_logconf_from_yaml,
@@ -20,6 +19,7 @@
2019
ensure_connection_to_public_endpoint,
2120
ensure_response_from_backend,
2221
patch_namespace_with_label,
22+
retry_get_until_body_contains,
2323
wait_before_test,
2424
wait_until_all_pods_are_ready,
2525
)
@@ -156,8 +156,8 @@ def test_responses(self, request, kube_apis, crd_ingress_controller_with_ap, bac
156156
ensure_response_from_backend(backend_setup.req_url, backend_setup.ingress_host, check404=True)
157157

158158
print("----------------------- Send request ----------------------")
159-
resp = requests.get(
160-
f"{backend_setup.req_url}/test.bat", headers={"host": backend_setup.ingress_host}, verify=False
159+
resp = retry_get_until_body_contains(
160+
f"{backend_setup.req_url}/test.bat", backend_setup.ingress_host, valid_resp_body
161161
)
162162

163163
print(resp.text)
@@ -176,17 +176,9 @@ def test_responses(self, request, kube_apis, crd_ingress_controller_with_ap, bac
176176
ensure_response_from_backend(backend_setup.req_url, backend_setup.ingress_host, check404=True)
177177

178178
print("----------------------- Send request ----------------------")
179-
resp = requests.get(
180-
f"{backend_setup.req_url}/test.bat", headers={"host": backend_setup.ingress_host}, verify=False
179+
resp = retry_get_until_body_contains(
180+
f"{backend_setup.req_url}/test.bat", backend_setup.ingress_host, invalid_resp_body
181181
)
182-
retry = 0
183-
while invalid_resp_body not in resp.text and retry <= 60:
184-
resp = requests.get(
185-
f"{backend_setup.req_url}/test.bat", headers={"host": backend_setup.ingress_host}, verify=False
186-
)
187-
retry += 1
188-
wait_before_test(1)
189-
print(f"Policy not yet enforced, retrying... #{retry}")
190182

191183
assert invalid_resp_body in resp.text
192184
assert resp.status_code == 200
@@ -202,17 +194,9 @@ def test_responses(self, request, kube_apis, crd_ingress_controller_with_ap, bac
202194
ensure_response_from_backend(backend_setup.req_url, backend_setup.ingress_host, check404=True)
203195

204196
print("----------------------- Send request ----------------------")
205-
resp = requests.get(
206-
f"{backend_setup.req_url}/test.bat", headers={"host": backend_setup.ingress_host}, verify=False
197+
resp = retry_get_until_body_contains(
198+
f"{backend_setup.req_url}/test.bat", backend_setup.ingress_host, valid_resp_body
207199
)
208-
retry = 0
209-
while valid_resp_body not in resp.text and retry <= 60:
210-
resp = requests.get(
211-
f"{backend_setup.req_url}/test.bat", headers={"host": backend_setup.ingress_host}, verify=False
212-
)
213-
retry += 1
214-
wait_before_test(1)
215-
print(f"Policy not yet removed, retrying... #{retry}")
216200

217201
assert valid_resp_body in resp.text
218202
assert resp.status_code == 200

0 commit comments

Comments
 (0)