diff --git a/testsuite/httpx/__init__.py b/testsuite/httpx/__init__.py index 05c06104..90e25ac6 100644 --- a/testsuite/httpx/__init__.py +++ b/testsuite/httpx/__init__.py @@ -50,6 +50,7 @@ def should_backoff(self): or (self.error is None and self.status_code in self.retry_codes) or self.has_error("Server disconnected without sending a response.") or self.has_error("timed out") + or self.has_error("SSL: UNEXPECTED_EOF_WHILE_READING") ) def has_error(self, error_msg: str) -> bool: diff --git a/testsuite/tests/singlecluster/gateway/reconciliation/listeners/__init__.py b/testsuite/tests/singlecluster/gateway/reconciliation/listeners/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/testsuite/tests/singlecluster/gateway/reconciliation/listeners/conftest.py b/testsuite/tests/singlecluster/gateway/reconciliation/listeners/conftest.py new file mode 100644 index 00000000..ef320462 --- /dev/null +++ b/testsuite/tests/singlecluster/gateway/reconciliation/listeners/conftest.py @@ -0,0 +1,65 @@ +""" +Conftest for Gateway listeners tests. +The main change consists of replacing the default wildcard domain for an exact one. +""" + +import pytest + +from testsuite.gateway.gateway_api.hostname import StaticHostname + + +@pytest.fixture(scope="module") +def domain_prefix(): + """Prefix of default domain.""" + return "prefix" + + +@pytest.fixture(scope="module") +def wildcard_domain(base_domain, domain_prefix, blame): + """ + For these tests we want specific default domain, not wildcard. + """ + return f'{blame(domain_prefix + "1")}.{base_domain}' + + +@pytest.fixture(scope="module") +def second_domain(base_domain, domain_prefix, blame): + """Second domain string, not used in any object yet. To be assigned inside test.""" + return f'{blame(domain_prefix + "2")}.{base_domain}' + + +@pytest.fixture(scope="module") +def custom_client(gateway): + """ + While changing TLS listeners the TLS certificate changes so a new client needs to be generated + to fetch newest tls cert from cluster. + """ + + def _client_new(hostname: str): + return StaticHostname(hostname, gateway.get_tls_cert).client() + + return _client_new + + +@pytest.fixture(scope="module") +def check_ok_https(custom_client, auth): + """ + Assert that HTTPS connection to domain works and returns 200. Authorization is used. + Assert that no DNS and TLS errors happened. + """ + + def _check_ok_https(domain: str): + response = custom_client(domain).get("/get", auth=auth) + assert not response.has_dns_error() + assert not response.has_cert_verify_error() + assert response.status_code == 200 + + return _check_ok_https + + +@pytest.fixture(scope="module") +def route(route, wildcard_domain): + """Ensure that route hostname matches the gateway hostname.""" + route.remove_all_hostnames() + route.add_hostname(wildcard_domain) + return route diff --git a/testsuite/tests/singlecluster/gateway/reconciliation/listeners/test_gateway_basic_listeners.py b/testsuite/tests/singlecluster/gateway/reconciliation/listeners/test_gateway_basic_listeners.py new file mode 100644 index 00000000..54e8046b --- /dev/null +++ b/testsuite/tests/singlecluster/gateway/reconciliation/listeners/test_gateway_basic_listeners.py @@ -0,0 +1,53 @@ +""" +Test case: +- Add new listener and add it to HTTPRoute and test both work +- Remove the new listener and remove it from HTTPRoute and test removed one is not working +""" + +from time import sleep +import pytest + +from testsuite.gateway import TLSGatewayListener +from testsuite.utils import is_nxdomain + + +pytestmark = [pytest.mark.kuadrant_only, pytest.mark.dnspolicy, pytest.mark.tlspolicy] + + +@pytest.fixture(scope="module") +def domain_prefix(): + """Prefix for this test""" + return "listen" + + +LISTENER_NAME = "api-second" + + +def test_listeners(custom_client, check_ok_https, gateway, route, wildcard_domain, second_domain): + """ + This test checks reconciliation of dns/tls policy on addition and removal of listeners in gateway and HTTPRoute. + """ + + # Check the default domain works and second domain does not exist yet + check_ok_https(wildcard_domain) + assert is_nxdomain(second_domain) + assert custom_client(second_domain).get("/get").has_dns_error() + + # Add second domain to gateway and route + gateway.add_listener(TLSGatewayListener(hostname=second_domain, gateway_name=gateway.name(), name=LISTENER_NAME)) + route.add_hostname(second_domain) + + # Check both domains work + for domain in [wildcard_domain, second_domain]: + check_ok_https(domain) + + # Remove second domain, store TTL value of to be removed DNS record + second_domain_ttl = gateway.get_listener_dns_ttl(LISTENER_NAME) + route.remove_hostname(second_domain) + gateway.remove_listener(LISTENER_NAME) + + # Check the default domain still works and second domain does not exist anymore + sleep(second_domain_ttl) + check_ok_https(wildcard_domain) + assert is_nxdomain(second_domain) + assert custom_client(second_domain).get("/get").has_dns_error() diff --git a/testsuite/tests/singlecluster/gateway/reconciliation/listeners/test_gateway_listeners_dns.py b/testsuite/tests/singlecluster/gateway/reconciliation/listeners/test_gateway_listeners_dns.py new file mode 100644 index 00000000..d2f79e3a --- /dev/null +++ b/testsuite/tests/singlecluster/gateway/reconciliation/listeners/test_gateway_listeners_dns.py @@ -0,0 +1,41 @@ +"""Testing specific bug that happens when listener hostname in Gateway gets changed while DNSPolicy is applied.""" + +from time import sleep +import pytest + +from testsuite.gateway import TLSGatewayListener +from testsuite.utils import is_nxdomain + +pytestmark = [pytest.mark.kuadrant_only, pytest.mark.dnspolicy, pytest.mark.tlspolicy] + + +@pytest.fixture(scope="module") +def domain_prefix(): + """Prefix for this test""" + return "dnsbug" + + +DEFAULT_LISTENER_NAME = TLSGatewayListener.name + + +@pytest.mark.issue("https://github.com/Kuadrant/kuadrant-operator/issues/794") +def test_change_listener(custom_client, check_ok_https, gateway, route, second_domain, wildcard_domain): + """ + This test checks if after change of listener hostname in a Gateway while having DNSPolicy applied, that + the old hostname gets deleted from DNS provider. After editing the hostname in HTTPRoute to the new value + this test checks the reconciliation of such procedure. + """ + check_ok_https(wildcard_domain) + wildcard_domain_ttl = gateway.get_listener_dns_ttl(DEFAULT_LISTENER_NAME) + + gateway.remove_listener(DEFAULT_LISTENER_NAME) + route.remove_hostname(wildcard_domain) + gateway.add_listener( + TLSGatewayListener(hostname=second_domain, gateway_name=gateway.name(), name=DEFAULT_LISTENER_NAME) + ) + route.add_hostname(second_domain) + + sleep(wildcard_domain_ttl) + check_ok_https(second_domain) + assert is_nxdomain(wildcard_domain) + assert custom_client(wildcard_domain).get("/get").has_dns_error()