From 26301b194ebfd55cc7cc20efc7901bfe5d35c69e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20=C4=8C=C3=A1p?= Date: Mon, 16 Oct 2023 12:41:26 +0200 Subject: [PATCH 1/4] Add CustomReference class --- .../openshift/objects/gateway_api/__init__.py | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/testsuite/openshift/objects/gateway_api/__init__.py b/testsuite/openshift/objects/gateway_api/__init__.py index 6d7e457d..a08fe5c5 100644 --- a/testsuite/openshift/objects/gateway_api/__init__.py +++ b/testsuite/openshift/objects/gateway_api/__init__.py @@ -1,5 +1,9 @@ """Module containing all Gateway API related classes""" from abc import ABC, abstractmethod +from dataclasses import dataclass +from typing import Any, Optional + +from testsuite.objects import asdict class Referencable(ABC): @@ -12,3 +16,22 @@ def reference(self) -> dict[str, str]: Returns dict, which can be used as reference in Gateway API Objects. https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.ParentReference """ + + +@dataclass +class CustomReference(Referencable): + """ + Manually creates Reference object. + https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io%2fv1beta1.ParentReference + """ + + @property + def reference(self) -> dict[str, Any]: + return asdict(self) + + group: str + kind: str + name: str + namespace: Optional[str] = None + sectionName: Optional[str] = None # pylint: disable=invalid-name + port: Optional[int] = None From 079556a794a134ca9fc8510ec91acd18049f5b36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20=C4=8C=C3=A1p?= Date: Mon, 16 Oct 2023 14:13:19 +0200 Subject: [PATCH 2/4] Add TLS to MGCGateway --- .../openshift/objects/gateway_api/gateway.py | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/testsuite/openshift/objects/gateway_api/gateway.py b/testsuite/openshift/objects/gateway_api/gateway.py index e8e5f432..c5f319b4 100644 --- a/testsuite/openshift/objects/gateway_api/gateway.py +++ b/testsuite/openshift/objects/gateway_api/gateway.py @@ -4,6 +4,7 @@ from openshift import Selector, timeout, selector +from testsuite.certificates import Certificate from testsuite.openshift.client import OpenShiftClient from testsuite.openshift.objects import OpenShiftObject from testsuite.openshift.objects.proxy import Proxy @@ -93,7 +94,33 @@ def create_instance( if placement is not None: labels["cluster.open-cluster-management.io/placement"] = placement - return super(MGCGateway, cls).create_instance(openshift, name, gateway_class, hostname, labels) + instance = super(MGCGateway, cls).create_instance(openshift, name, gateway_class, hostname, labels) + instance.model["spec"]["listeners"] = [ + { + "name": "api", + "port": 443, + "protocol": "HTTPS", + "hostname": hostname, + "allowedRoutes": {"namespaces": {"from": "All"}}, + "tls": { + "mode": "Terminate", + "certificateRefs": [{"name": f"{name}-tls", "kind": "Secret"}], + }, + } + ] + + return instance + + def get_tls_cert(self) -> Certificate: + """Returns TLS certificate used by the gateway""" + tls_cert_secret_name = self.model.spec.listeners[0].tls.certificateRefs[0].name + tls_cert_secret = self.openshift.get_secret(tls_cert_secret_name) + tls_cert = Certificate( + key=tls_cert_secret["tls.key"], + certificate=tls_cert_secret["tls.crt"], + chain=tls_cert_secret["ca.crt"], + ) + return tls_cert def get_spoke_gateway(self, spokes: dict[str, OpenShiftClient]) -> "MGCGateway": """ From 639d288c2885ac9d1933dfabd9db83840633e82a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20=C4=8C=C3=A1p?= Date: Mon, 16 Oct 2023 15:25:50 +0200 Subject: [PATCH 3/4] Add TLSPolicy --- testsuite/openshift/objects/tlspolicy.py | 31 ++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 testsuite/openshift/objects/tlspolicy.py diff --git a/testsuite/openshift/objects/tlspolicy.py b/testsuite/openshift/objects/tlspolicy.py new file mode 100644 index 00000000..a419590f --- /dev/null +++ b/testsuite/openshift/objects/tlspolicy.py @@ -0,0 +1,31 @@ +"""Module for TLSPolicy related classes""" +from testsuite.openshift.client import OpenShiftClient +from testsuite.openshift.objects import OpenShiftObject +from testsuite.openshift.objects.gateway_api import Referencable + + +class TLSPolicy(OpenShiftObject): + """TLSPolicy object""" + + @classmethod + def create_instance( + cls, + openshift: OpenShiftClient, + name: str, + parent: Referencable, + issuer: Referencable, + labels: dict[str, str] = None, + ): + """Creates new instance of TLSPolicy""" + + model = { + "apiVersion": "kuadrant.io/v1alpha1", + "kind": "TLSPolicy", + "metadata": {"name": name, "labels": labels}, + "spec": { + "targetRef": parent.reference, + "issuerRef": issuer.reference, + }, + } + + return cls(model, context=openshift.context) From ef90dc234ff2b7167265c6222794dc4aa2587f08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20=C4=8C=C3=A1p?= Date: Mon, 16 Oct 2023 15:26:31 +0200 Subject: [PATCH 4/4] Make mgc test_smoke use TLSPolicy --- testsuite/tests/mgc/conftest.py | 39 +++++++++++++++++++++++++++---- testsuite/tests/mgc/test_basic.py | 10 ++++++-- 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/testsuite/tests/mgc/conftest.py b/testsuite/tests/mgc/conftest.py index 2c6a0fcf..bfc9044a 100644 --- a/testsuite/tests/mgc/conftest.py +++ b/testsuite/tests/mgc/conftest.py @@ -5,10 +5,12 @@ from testsuite.openshift.httpbin import Httpbin from testsuite.openshift.objects.dnspolicy import DNSPolicy +from testsuite.openshift.objects.gateway_api import CustomReference from testsuite.openshift.objects.gateway_api.gateway import MGCGateway, GatewayProxy from testsuite.openshift.objects.gateway_api.route import HTTPRoute from testsuite.openshift.objects.proxy import Proxy from testsuite.openshift.objects.route import Route +from testsuite.openshift.objects.tlspolicy import TLSPolicy @pytest.fixture(scope="module") @@ -41,7 +43,8 @@ def upstream_gateway(request, openshift, blame, hostname, module_label): ) request.addfinalizer(upstream_gateway.delete) upstream_gateway.commit() - upstream_gateway.wait_for_ready() + # we cannot wait here because of referencing not yet existent tls secret which would be provided later by tlspolicy + # upstream_gateway.wait_for_ready() return upstream_gateway @@ -61,6 +64,16 @@ def initial_host(hostname): return f"route.{hostname}" +@pytest.fixture(scope="session") +def self_signed_cluster_issuer(): + """Reference to cluster self-signed certificate issuer""" + return CustomReference( + group="cert-manager.io", + kind="ClusterIssuer", + name="selfsigned-cluster-issuer", + ) + + @pytest.fixture(scope="module") def route(request, proxy, blame, gateway, initial_host, backend) -> Route: """Exposed Route object""" @@ -77,9 +90,12 @@ def route(request, proxy, blame, gateway, initial_host, backend) -> Route: return route +# pylint: disable=unused-argument @pytest.fixture(scope="module") -def gateway(upstream_gateway, spokes): +def gateway(upstream_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 + upstream_gateway.wait_for_ready() gw = upstream_gateway.get_spoke_gateway(spokes) gw.wait_for_ready() return gw @@ -108,10 +124,23 @@ def dns_policy(blame, upstream_gateway, module_label): return policy -@pytest.fixture(scope="module", autouse=True) -def commit(request, dns_policy): +@pytest.fixture(scope="module") +def tls_policy(blame, upstream_gateway, module_label, self_signed_cluster_issuer): + """TLSPolicy fixture""" + policy = TLSPolicy.create_instance( + upstream_gateway.openshift, + blame("tls"), + parent=upstream_gateway, + issuer=self_signed_cluster_issuer, + labels={"app": module_label}, + ) + return policy + + +@pytest.fixture(scope="module") +def hub_policies_commit(request, upstream_gateway, dns_policy, tls_policy): """Commits all important stuff before tests""" - for component in [dns_policy]: + for component in [dns_policy, tls_policy]: if component is not None: request.addfinalizer(component.delete) component.commit() diff --git a/testsuite/tests/mgc/test_basic.py b/testsuite/tests/mgc/test_basic.py index 235d9a50..731a62a2 100644 --- a/testsuite/tests/mgc/test_basic.py +++ b/testsuite/tests/mgc/test_basic.py @@ -17,6 +17,8 @@ import pytest +from testsuite.httpx import HttpxBackoffClient + pytestmark = [pytest.mark.mgc] @@ -25,12 +27,16 @@ def test_gateway_readiness(gateway): assert gateway.is_ready() -def test_smoke(route): +def test_smoke(route, upstream_gateway): """ Tests whether the backend, exposed using the HTTPRoute and Gateway, was exposed correctly, having a tls secured endpoint with a hostname managed by MGC """ - backend_client = route.client(verify=False) # self-signed certificate; TBD + + tls_cert = upstream_gateway.get_tls_cert() + + # assert that tls_cert is used by the server + backend_client = HttpxBackoffClient(base_url=f"https://{route.hostnames[0]}", verify=tls_cert) sleep(30) # wait for DNS record to propagate correctly; TBD