From 23e4b6510f9050364f0dd02403e40e17ffaa4244 Mon Sep 17 00:00:00 2001 From: phala Date: Thu, 15 Feb 2024 16:20:39 +0100 Subject: [PATCH] Unify exposers - Add both passthrough and verify as arguments to Exposer - Require singular constructor - Tie base_domain to exposer - Allow specifying default_exposer and managedzone in settings --- config/settings.local.yaml.tpl | 1 + config/settings.yaml | 3 +++ testsuite/config/__init__.py | 12 +++++++-- testsuite/config/exposer.py | 14 ++++++++++ testsuite/gateway/__init__.py | 11 ++++++++ testsuite/gateway/exposers.py | 15 +++++++---- testsuite/gateway/gateway_api/hostname.py | 16 +++++++---- testsuite/openshift/route.py | 5 ++++ testsuite/tests/conftest.py | 15 +++++++---- .../authorino/operator/tls/conftest.py | 7 ++--- testsuite/tests/mgc/conftest.py | 27 ++++++++----------- .../test_invalid_issuer_reference.py | 9 ------- .../mgc/reconciliation/test_same_target.py | 9 ------- testsuite/tests/mgc/test_external_ca.py | 12 ++++----- 14 files changed, 95 insertions(+), 61 deletions(-) create mode 100644 testsuite/config/exposer.py diff --git a/config/settings.local.yaml.tpl b/config/settings.local.yaml.tpl index c8e8186d..6413fcde 100644 --- a/config/settings.local.yaml.tpl +++ b/config/settings.local.yaml.tpl @@ -41,6 +41,7 @@ # auth_url: "" # authorization URL for already deployed Authorino # oidc_url: "" # oidc URL for already deployed Authorino # metrics_service_name: "" # controller metrics service name for already deployed Authorino +# default_exposer: "openshift" # Exposer type that should be used, options: 'openshift' # control_plane: # hub: # Hub cluster # project: "multi-cluster-gateways" # Optional: namespace where MGC resources are created and where the hub gateway will be created diff --git a/config/settings.yaml b/config/settings.yaml index d53165e0..c87f67e8 100644 --- a/config/settings.yaml +++ b/config/settings.yaml @@ -25,3 +25,6 @@ default: hyperfoil: generate_reports: True reports_dir: "reports" + default_exposer: "openshift" + control_plane: + managedzone: "aws-mz" diff --git a/testsuite/config/__init__.py b/testsuite/config/__init__.py index 95841330..599cd1b8 100644 --- a/testsuite/config/__init__.py +++ b/testsuite/config/__init__.py @@ -38,10 +38,18 @@ def __init__(self, name, default, **kwargs) -> None: Validator("service_protection.authorino.auth_url", must_exist=True) & Validator("service_protection.authorino.oidc_url", must_exist=True) ), + Validator( + "default_exposer", + # If exposer was successfully converted, it will no longer be a string""" + condition=lambda exposer: not isinstance(exposer, str), + must_exist=True, + messages={"condition": "{value} is not valid exposer"}, + ), + Validator("control_plane.managedzone", must_exist=True, ne=None), DefaultValueValidator("rhsso.url", default=fetch_route("no-ssl-sso")), DefaultValueValidator("rhsso.password", default=fetch_secret("credential-sso", "ADMIN_PASSWORD")), DefaultValueValidator("mockserver.url", default=fetch_route("mockserver", force_http=True)), ], - validate_only=["authorino", "kuadrant"], - loaders=["dynaconf.loaders.env_loader", "testsuite.config.openshift_loader"], + validate_only=["authorino", "kuadrant", "default_exposer", "control_plane"], + loaders=["dynaconf.loaders.env_loader", "testsuite.config.openshift_loader", "testsuite.config.exposer"], ) diff --git a/testsuite/config/exposer.py b/testsuite/config/exposer.py new file mode 100644 index 00000000..4ac20007 --- /dev/null +++ b/testsuite/config/exposer.py @@ -0,0 +1,14 @@ +"""Translates string to an Exposer class that can initialized""" + +from testsuite.gateway.exposers import OpenShiftExposer + +EXPOSERS = {"openshift": OpenShiftExposer} + + +# pylint: disable=unused-argument +def load(obj, env=None, silent=True, key=None, filename=None): + """Creates all OpenShift clients""" + try: + obj["default_exposer"] = EXPOSERS[obj["default_exposer"]] + except KeyError: + return diff --git a/testsuite/gateway/__init__.py b/testsuite/gateway/__init__.py index ab66fda6..5bb14780 100644 --- a/testsuite/gateway/__init__.py +++ b/testsuite/gateway/__init__.py @@ -191,9 +191,20 @@ def hostname(self) -> str: class Exposer(LifecycleObject): """Exposes hostnames to be accessible from outside""" + def __init__(self, openshift): + super().__init__() + self.openshift = openshift + self.passthrough = False + self.verify = None + @abstractmethod def expose_hostname(self, name, gateway: Gateway) -> Hostname: """ Exposes hostname, so it is accessible from outside Actual hostname is generated from "name" and is returned in a form of a Hostname object """ + + @property + @abstractmethod + def base_domain(self) -> str: + """Returns base domains for all hostnames created by this exposer""" diff --git a/testsuite/gateway/exposers.py b/testsuite/gateway/exposers.py index e37cd708..f4b8025b 100644 --- a/testsuite/gateway/exposers.py +++ b/testsuite/gateway/exposers.py @@ -1,15 +1,19 @@ +"""General exposers, not tied to Envoy or Gateway API""" + from testsuite.gateway import Exposer, Gateway, Hostname -from testsuite.lifecycle import LifecycleObject from testsuite.openshift.route import OpenshiftRoute -class OpenShiftExposer(Exposer, LifecycleObject): +class OpenShiftExposer(Exposer): """Exposes hostnames through OpenShift Route objects""" - def __init__(self, passthrough=False) -> None: - super().__init__() + def __init__(self, openshift) -> None: + super().__init__(openshift) self.routes: list[OpenshiftRoute] = [] - self.passthrough = passthrough + + @property + def base_domain(self) -> str: + return self.openshift.apps_url def expose_hostname(self, name, gateway: Gateway) -> Hostname: tls = False @@ -20,6 +24,7 @@ def expose_hostname(self, name, gateway: Gateway) -> Hostname: route = OpenshiftRoute.create_instance( gateway.openshift, name, gateway.service_name, "api", tls=tls, termination=termination ) + route.verify = self.verify self.routes.append(route) route.commit() return route diff --git a/testsuite/gateway/gateway_api/hostname.py b/testsuite/gateway/gateway_api/hostname.py index 7c69d848..e4209fa4 100644 --- a/testsuite/gateway/gateway_api/hostname.py +++ b/testsuite/gateway/gateway_api/hostname.py @@ -1,10 +1,15 @@ """Module containing implementation for Hostname related classes of Gateway API""" +from functools import cached_property + from httpx import Client +from openshift_client import selector from testsuite.certificates import Certificate +from testsuite.config import settings from testsuite.httpx import KuadrantClient from testsuite.gateway import Gateway, Hostname, Exposer +from testsuite.utils import generate_tail class StaticHostname(Hostname): @@ -30,14 +35,15 @@ def hostname(self): class DNSPolicyExposer(Exposer): """Exposing is done as part of DNSPolicy, so no work needs to be done here""" - def __init__(self, base_domain, tls_cert: Certificate = None): - super().__init__() - self.base_domain = base_domain - self.tls_cert = tls_cert + @cached_property + def base_domain(self) -> str: + mz_name = settings["control_plane"]["managedzone"] + zone = selector(f"managedzone/{mz_name}", static_context=self.openshift.context).object() + return f'{generate_tail(5)}.{zone.model["spec"]["domainName"]}' def expose_hostname(self, name, gateway: Gateway) -> Hostname: return StaticHostname( - f"{name}.{self.base_domain}", gateway.get_tls_cert() if self.tls_cert is None else self.tls_cert + f"{name}.{self.base_domain}", gateway.get_tls_cert() if self.verify is None else self.verify ) def commit(self): diff --git a/testsuite/openshift/route.py b/testsuite/openshift/route.py index 9fdfdd32..3be492d0 100644 --- a/testsuite/openshift/route.py +++ b/testsuite/openshift/route.py @@ -12,6 +12,10 @@ class OpenshiftRoute(OpenShiftObject, Hostname): """Openshift Route object""" + def __init__(self, dict_to_model=None, string_to_model=None, context=None): + super().__init__(dict_to_model, string_to_model, context) + self.verify = None + @classmethod def create_instance( cls, @@ -41,6 +45,7 @@ def client(self, **kwargs) -> Client: protocol = "http" if "tls" in self.model.spec: protocol = "https" + kwargs.setdefault("verify", self.verify) return KuadrantClient(base_url=f"{protocol}://{self.hostname}", **kwargs) @cached_property diff --git a/testsuite/tests/conftest.py b/testsuite/tests/conftest.py index b1b46147..d915578e 100644 --- a/testsuite/tests/conftest.py +++ b/testsuite/tests/conftest.py @@ -19,7 +19,6 @@ from testsuite.gateway.envoy import Envoy from testsuite.gateway.envoy.route import EnvoyVirtualRoute from testsuite.gateway.gateway_api.gateway import KuadrantGateway -from testsuite.gateway.exposers import OpenShiftExposer from testsuite.gateway.gateway_api.route import HTTPRoute from testsuite.utils import randomize, _whoami @@ -315,9 +314,9 @@ def route(request, kuadrant, gateway, blame, hostname, backend, module_label) -> @pytest.fixture(scope="module") -def exposer(request) -> Exposer: +def exposer(request, testconfig, hub_openshift) -> Exposer: """Exposer object instance""" - exposer = OpenShiftExposer() + exposer = testconfig["default_exposer"](hub_openshift) request.addfinalizer(exposer.delete) exposer.commit() return exposer @@ -331,11 +330,17 @@ def hostname(gateway, exposer, blame) -> Hostname: @pytest.fixture(scope="module") -def wildcard_domain(openshift): +def base_domain(exposer): + """Returns preconfigured base domain""" + return exposer.base_domain + + +@pytest.fixture(scope="module") +def wildcard_domain(base_domain): """ Wildcard domain of openshift cluster """ - return f"*.{openshift.apps_url}" + return f"*.{base_domain}" @pytest.fixture(scope="module") diff --git a/testsuite/tests/kuadrant/authorino/operator/tls/conftest.py b/testsuite/tests/kuadrant/authorino/operator/tls/conftest.py index f1e3036f..b4012e0e 100644 --- a/testsuite/tests/kuadrant/authorino/operator/tls/conftest.py +++ b/testsuite/tests/kuadrant/authorino/operator/tls/conftest.py @@ -78,7 +78,7 @@ def _create_secret(certificate: Certificate, name: str, labels: Optional[Dict[st @pytest.fixture(scope="module") def authorino_domain(openshift): """ - Hostname of the upstream certificate sent to be validated by APIcast + Hostname of the upstream certificate sent to be validated by Envoy May be overwritten to configure different test cases """ return f"*.{openshift.project}.svc.cluster.local" @@ -181,9 +181,10 @@ def gateway( @pytest.fixture(scope="module") -def exposer(request) -> Exposer: +def exposer(request, hub_openshift) -> Exposer: """Exposer object instance with TLS passthrough""" - exposer = OpenShiftExposer(passthrough=True) + exposer = OpenShiftExposer(hub_openshift) + exposer.passthrough = True request.addfinalizer(exposer.delete) exposer.commit() return exposer diff --git a/testsuite/tests/mgc/conftest.py b/testsuite/tests/mgc/conftest.py index d8cc0d73..759a10cf 100644 --- a/testsuite/tests/mgc/conftest.py +++ b/testsuite/tests/mgc/conftest.py @@ -1,7 +1,6 @@ """Conftest for MGC tests""" import pytest -from openshift_client import selector from weakget import weakget from testsuite.gateway import CustomReference, GatewayRoute, Exposer @@ -10,7 +9,6 @@ from testsuite.gateway.gateway_api.hostname import DNSPolicyExposer from testsuite.gateway.gateway_api.route import HTTPRoute from testsuite.policy.tls_policy import TLSPolicy -from testsuite.utils import generate_tail @pytest.fixture(scope="session") @@ -31,13 +29,13 @@ def hub_openshift(testconfig): @pytest.fixture(scope="module") -def hub_gateway(request, hub_openshift, blame, base_domain, module_label) -> MGCGateway: +def hub_gateway(request, hub_openshift, blame, wildcard_domain, module_label) -> MGCGateway: """Creates and returns configured and ready Hub Gateway""" hub_gateway = MGCGateway.create_instance( openshift=hub_openshift, name=blame("mgc-gateway"), gateway_class="kuadrant-multi-cluster-gateway-instance-per-cluster", - hostname=f"*.{base_domain}", + hostname=wildcard_domain, tls=True, placement="http-gateway", labels={"app": module_label}, @@ -73,17 +71,23 @@ def route(request, gateway, blame, hostname, backend, module_label) -> GatewayRo @pytest.fixture(scope="module") -def exposer(base_domain, hub_gateway) -> Exposer: +def exposer(request, hub_openshift) -> Exposer: """DNSPolicyExposer setup with expected TLS certificate""" - return DNSPolicyExposer(base_domain, tls_cert=hub_gateway.get_tls_cert()) + exposer = DNSPolicyExposer(hub_openshift) + request.addfinalizer(exposer.delete) + exposer.commit() + return exposer # pylint: disable=unused-argument @pytest.fixture(scope="module") -def gateway(hub_gateway, spokes, hub_policies_commit): +def gateway(exposer, hub_gateway, spokes, hub_policies_commit): """Downstream gateway, e.g. gateway on a spoke cluster""" # wait for upstream gateway here to be able to get spoke gateways hub_gateway.wait_for_ready() + # Only here is Hub Gateway ready, which means only here we can assign verify + exposer.verify = hub_gateway.get_tls_cert() + gw = hub_gateway.get_spoke_gateway(spokes) gw.wait_for_ready() return gw @@ -95,15 +99,6 @@ def openshift(gateway): return gateway.openshift -@pytest.fixture(scope="module", params=["aws-mz", "gcp-mz"]) -def base_domain(request, hub_openshift): - """Returns preconfigured base domain""" - mz_name = request.param - - zone = selector(f"managedzone/{mz_name}", static_context=hub_openshift.context).object() - return f"{generate_tail()}.{zone.model['spec']['domainName']}" - - @pytest.fixture(scope="module") def dns_policy(blame, hub_gateway, module_label): """DNSPolicy fixture""" diff --git a/testsuite/tests/mgc/reconciliation/test_invalid_issuer_reference.py b/testsuite/tests/mgc/reconciliation/test_invalid_issuer_reference.py index ed1d2f15..47c87b45 100644 --- a/testsuite/tests/mgc/reconciliation/test_invalid_issuer_reference.py +++ b/testsuite/tests/mgc/reconciliation/test_invalid_issuer_reference.py @@ -1,22 +1,13 @@ """Tests that TLSPolicy is rejected if the issuer is invalid""" import pytest -from openshift_client import selector from testsuite.gateway import CustomReference from testsuite.policy.tls_policy import TLSPolicy -from testsuite.utils import generate_tail pytestmark = [pytest.mark.mgc] -@pytest.fixture(scope="module") -def base_domain(hub_openshift): - """Returns preconfigured base domain""" - zone = selector("managedzone/aws-mz", static_context=hub_openshift.context).object() - return f"{generate_tail()}.{zone.model['spec']['domainName']}" - - def test_wrong_issuer_type(request, hub_gateway, hub_openshift, blame, module_label): """Tests that TLSPolicy is rejected if issuer does not have a correct type""" diff --git a/testsuite/tests/mgc/reconciliation/test_same_target.py b/testsuite/tests/mgc/reconciliation/test_same_target.py index 31486b63..b4ca9aa3 100644 --- a/testsuite/tests/mgc/reconciliation/test_same_target.py +++ b/testsuite/tests/mgc/reconciliation/test_same_target.py @@ -1,22 +1,13 @@ """Tests that DNSPolicy/TLSPolicy is rejected when the Gateway already has a policy of the same kind""" import pytest -from openshift_client import selector from testsuite.policy.tls_policy import TLSPolicy from testsuite.tests.mgc.reconciliation import dns_policy -from testsuite.utils import generate_tail pytestmark = [pytest.mark.mgc] -@pytest.fixture(scope="module") -def base_domain(hub_openshift): - """Returns preconfigured base domain""" - zone = selector("managedzone/aws-mz", static_context=hub_openshift.context).object() - return f"{generate_tail()}.{zone.model['spec']['domainName']}" - - @pytest.mark.parametrize( "create_cr", [pytest.param(dns_policy, id="DNSPolicy"), pytest.param(TLSPolicy.create_instance, id="TLSPolicy")] ) diff --git a/testsuite/tests/mgc/test_external_ca.py b/testsuite/tests/mgc/test_external_ca.py index 2ea851a8..733c782f 100644 --- a/testsuite/tests/mgc/test_external_ca.py +++ b/testsuite/tests/mgc/test_external_ca.py @@ -37,8 +37,7 @@ from openshift_client import selector from openshift_client.model import OpenShiftPythonException -from testsuite.gateway import Exposer, CustomReference -from testsuite.gateway.gateway_api.hostname import DNSPolicyExposer +from testsuite.gateway import CustomReference pytestmark = [pytest.mark.mgc] @@ -58,15 +57,14 @@ def cluster_issuer(hub_openshift): @pytest.fixture(scope="module") -def exposer(base_domain, hub_gateway) -> Exposer: - """DNSPolicyExposer setup with expected TLS certificate""" +def gateway(gateway, exposer, hub_gateway): + """Only at this step we have TLS secret created and GW fully reconciled""" root_cert = resources.files("testsuite.resources").joinpath("letsencrypt-stg-root-x1.pem").read_text() old_cert = hub_gateway.get_tls_cert() - return DNSPolicyExposer(base_domain, tls_cert=dataclasses.replace(old_cert, chain=old_cert.certificate + root_cert)) + exposer.verify = dataclasses.replace(old_cert, chain=old_cert.certificate + root_cert) + return gateway -# Reduce scope of the base_domain fixture so the test only runs on aws-mz ManagedZone -@pytest.mark.parametrize("base_domain", ["aws-mz"], indirect=True) def test_smoke_letsencrypt(client): """ Tests whether the backend, exposed using the HTTPRoute and Gateway, was exposed correctly,