Skip to content

Commit 8b17f02

Browse files
javorszkypdabelf5
authored andcommitted
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.
1 parent e0c662a commit 8b17f02

2 files changed

Lines changed: 33 additions & 23 deletions

File tree

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

tests/suite/utils/resources_utils.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1948,6 +1948,32 @@ def ensure_response_from_backend(req_url, host, additional_headers=None, check40
19481948
pytest.fail(f"Keep getting 502|504 from {req_url} after 60 seconds. Exiting...")
19491949

19501950

1951+
def retry_get_until_body_contains(req_url, host, expected_body, retries=60, verify=False):
1952+
"""
1953+
Repeatedly GET req_url until expected_body appears in the response body.
1954+
1955+
Tolerates ConnectionError/RemoteDisconnected caused by NGINX reloads
1956+
(worker recycling during App Protect reconfiguration closes connections).
1957+
1958+
:param req_url: url to request
1959+
:param host: value for the Host header
1960+
:param expected_body: substring expected to appear in the response body
1961+
:param retries: number of retries at 1 second interval
1962+
:param verify: passed through to requests.get for TLS verification
1963+
:return: the final requests.Response (may not contain expected_body if exhausted)
1964+
"""
1965+
resp = None
1966+
for i in range(retries + 1):
1967+
try:
1968+
resp = requests.get(req_url, headers={"host": host}, verify=verify)
1969+
if expected_body in resp.text:
1970+
return resp
1971+
except requests.exceptions.ConnectionError as e:
1972+
print(f"Attempt {i + 1}: connection dropped during reload ({e})")
1973+
wait_before_test(1)
1974+
return resp
1975+
1976+
19511977
def get_service_endpoint(kube_apis, service_name, namespace) -> str:
19521978
"""
19531979
Wait for endpoint resource to spin up.

0 commit comments

Comments
 (0)