From 974a27e890b09b2402b2aa925b036ad6b1503e22 Mon Sep 17 00:00:00 2001 From: Alex Zgabur Date: Thu, 12 Sep 2024 12:21:41 +0200 Subject: [PATCH] Refactor and add gateway listener tests Signed-off-by: Alex Zgabur --- testsuite/httpx/__init__.py | 1 + .../reconciliation/listeners/__init__.py | 0 .../reconciliation/listeners/conftest.py | 59 +++++++++++++++++++ .../listeners/test_gateway_basic_listeners.py | 46 +++++++++++++++ .../listeners/test_gateway_listeners_dns.py | 34 +++++++++++ 5 files changed, 140 insertions(+) create mode 100644 testsuite/tests/singlecluster/gateway/reconciliation/listeners/__init__.py create mode 100644 testsuite/tests/singlecluster/gateway/reconciliation/listeners/conftest.py create mode 100644 testsuite/tests/singlecluster/gateway/reconciliation/listeners/test_gateway_basic_listeners.py create mode 100644 testsuite/tests/singlecluster/gateway/reconciliation/listeners/test_gateway_listeners_dns.py 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..23213035 --- /dev/null +++ b/testsuite/tests/singlecluster/gateway/reconciliation/listeners/conftest.py @@ -0,0 +1,59 @@ +""" +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 wildcard_domain(base_domain, blame): + """ + For these tests we want specific default domain, not wildcard. + """ + return f'{blame("prefix1")}.{base_domain}' + + +@pytest.fixture(scope="module") +def second_domain(base_domain, blame): + """Second domain string, not used in any object yet. To be assigned inside test.""" + return f'{blame("prefix2")}.{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..e0a65df2 --- /dev/null +++ b/testsuite/tests/singlecluster/gateway/reconciliation/listeners/test_gateway_basic_listeners.py @@ -0,0 +1,46 @@ +""" +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] + +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..ee5f9413 --- /dev/null +++ b/testsuite/tests/singlecluster/gateway/reconciliation/listeners/test_gateway_listeners_dns.py @@ -0,0 +1,34 @@ +"""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] + +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()