From dbb6aa070a5d45e3506693b1a2cc51fd56d93b17 Mon Sep 17 00:00:00 2001 From: phala Date: Tue, 29 Aug 2023 14:18:51 +0200 Subject: [PATCH] Rework testsuite to work with new spec.resources field - Add another EnvoyConfig class - Add parametrization - Reformat using black --- pyproject.toml | 5 +- testsuite/certificates/__init__.py | 21 ++- testsuite/config/openshift_loader.py | 5 +- testsuite/httpx/__init__.py | 8 +- testsuite/openshift/__init__.py | 6 +- testsuite/openshift/client.py | 38 +++--- testsuite/openshift/config.py | 188 ++++++++++++++++++++++++++ testsuite/openshift/envoy.py | 84 +----------- testsuite/openshift/httpbin.py | 6 +- testsuite/tests/conftest.py | 54 ++++++-- testsuite/tests/test_reject.py | 14 +- testsuite/tests/test_rollback.py | 22 ++- testsuite/tests/test_routes.py | 12 +- testsuite/tests/test_update_config.py | 66 +++++---- testsuite/tests/tls/conftest.py | 38 ++++-- testsuite/tests/tls/test_tls.py | 4 - 16 files changed, 381 insertions(+), 190 deletions(-) create mode 100644 testsuite/openshift/config.py diff --git a/pyproject.toml b/pyproject.toml index ad42027..cfbfb9e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,8 +30,11 @@ log_level = "INFO" junit_logging = "all" junit_family = "xunit2" +[tool.black] +line-length = 120 + [tool.pylint.FORMAT] -max-line-length = 120 +max-line-length = 125 disable = [ "duplicate-code", # reports false alarms AND can't be disabled locally; pylint issue #214 "fixme", # ignore TODOs diff --git a/testsuite/certificates/__init__.py b/testsuite/certificates/__init__.py index 117a217..e8c1626 100644 --- a/testsuite/certificates/__init__.py +++ b/testsuite/certificates/__init__.py @@ -57,7 +57,11 @@ def __init__(self, binary) -> None: self.binary = binary def _execute_command( - self, command: str, *args: str, stdin: Optional[str] = None, env: Optional[Dict[str, str]] = None + self, + command: str, + *args: str, + stdin: Optional[str] = None, + env: Optional[Dict[str, str]] = None, ): args = (self.binary, command, *args) try: @@ -85,7 +89,10 @@ def exists(self): return shutil.which(self.binary) def generate_key( - self, common_name: str, names: Optional[List[Dict[str, str]]] = None, hosts: Optional[Collection[str]] = None + self, + common_name: str, + names: Optional[List[Dict[str, str]]] = None, + hosts: Optional[Collection[str]] = None, ) -> UnsignedKey: """Generates unsigned key""" data: Dict[str, Any] = {"CN": common_name} @@ -109,7 +116,10 @@ def sign_intermediate_authority(self, key: UnsignedKey, certificate_authority: C *args, "-", stdin=key.csr, - env={"CA": certificate_authority.certificate, "KEY": certificate_authority.key}, + env={ + "CA": certificate_authority.certificate, + "KEY": certificate_authority.key, + }, ) return Certificate(key=key.key, certificate=result["cert"]) @@ -121,7 +131,10 @@ def sign(self, key: UnsignedKey, certificate_authority: Certificate) -> Certific "-ca-key=env:KEY", "-", stdin=key.csr, - env={"CA": certificate_authority.certificate, "KEY": certificate_authority.key}, + env={ + "CA": certificate_authority.certificate, + "KEY": certificate_authority.key, + }, ) return Certificate(key=key.key, certificate=result["cert"]) diff --git a/testsuite/config/openshift_loader.py b/testsuite/config/openshift_loader.py index 682cf77..3d465a6 100644 --- a/testsuite/config/openshift_loader.py +++ b/testsuite/config/openshift_loader.py @@ -10,6 +10,9 @@ def load(obj, env=None, silent=True, key=None, filename=None): config = weakget(obj) section = config["openshift"] client = OpenShiftClient( - section["project"] % None, section["api_url"] % None, section["token"] % None, section["kubeconfig_path"] % None + section["project"] % None, + section["api_url"] % None, + section["token"] % None, + section["kubeconfig_path"] % None, ) obj["openshift"] = client diff --git a/testsuite/httpx/__init__.py b/testsuite/httpx/__init__.py index 926a261..73ee909 100644 --- a/testsuite/httpx/__init__.py +++ b/testsuite/httpx/__init__.py @@ -29,7 +29,13 @@ def __init__(self, msg, response): class HttpxBackoffClient(Client): """Httpx client which retries unstable requests""" - def __init__(self, *, verify: Union[Certificate, bool] = True, cert: Certificate = None, **kwargs): + def __init__( + self, + *, + verify: Union[Certificate, bool] = True, + cert: Certificate = None, + **kwargs, + ): self.files = [] self.retry_codes = {503} _verify = None diff --git a/testsuite/openshift/__init__.py b/testsuite/openshift/__init__.py index bd37f5c..c717b97 100644 --- a/testsuite/openshift/__init__.py +++ b/testsuite/openshift/__init__.py @@ -10,12 +10,14 @@ class LifecycleObject(abc.ABC): @abc.abstractmethod def commit(self): """Commits resource. - if there is some reconciliation needed, the method should wait until it is all reconciled""" + if there is some reconciliation needed, the method should wait until it is all reconciled + """ @abc.abstractmethod def delete(self): """Removes resource, - if there is some reconciliation needed, the method should wait until it is all reconciled""" + if there is some reconciliation needed, the method should wait until it is all reconciled + """ class OpenShiftObject(APIObject): diff --git a/testsuite/openshift/client.py b/testsuite/openshift/client.py index 20d3773..a5406d1 100644 --- a/testsuite/openshift/client.py +++ b/testsuite/openshift/client.py @@ -11,9 +11,6 @@ from testsuite.certificates import Certificate -# from testsuite.openshift.types.routes import Routes -# from testsuite.openshift.types.secrets import Secrets - class ServiceTypes(enum.Enum): """Service types enum.""" @@ -30,7 +27,13 @@ class OpenShiftClient: # pylint: disable=too-many-public-methods - def __init__(self, project: str, api_url: str = None, token: str = None, kubeconfig_path: str = None): + def __init__( + self, + project: str, + api_url: str = None, + token: str = None, + kubeconfig_path: str = None, + ): self._project = project self._api_url = api_url self.token = token @@ -79,16 +82,6 @@ def connected(self): return False return True - # @cached_property - # def routes(self): - # """Return dict-like interface for Routes""" - # return Routes(self) - # - # @cached_property - # def secrets(self): - # """Return dict-like interface for Secrets""" - # return Secrets(self) - def do_action(self, verb: str, *args, auto_raise: bool = True, parse_output: bool = False): """Run an oc command.""" with self.context: @@ -136,12 +129,12 @@ def is_ready(self, selector: Selector): ) return success - # def create(self, model): - # """Creates """ - # with self.context: - # return oc.create(model, ["--save-config=true"]) - - def create_tls_secret(self, name: str, certificate: Certificate, labels: Optional[Dict[str, str]] = None): + def create_tls_secret( + self, + name: str, + certificate: Certificate, + labels: Optional[Dict[str, str]] = None, + ): """Creates a TLS secret""" model: Dict = { "kind": "Secret", @@ -149,7 +142,10 @@ def create_tls_secret(self, name: str, certificate: Certificate, labels: Optiona "metadata": { "name": name, }, - "stringData": {"tls.crt": certificate.chain or certificate.certificate, "tls.key": certificate.key}, + "stringData": { + "tls.crt": certificate.chain or certificate.certificate, + "tls.key": certificate.key, + }, "type": "kubernetes.io/tls", } if labels is not None: diff --git a/testsuite/openshift/config.py b/testsuite/openshift/config.py new file mode 100644 index 0000000..f8b2a28 --- /dev/null +++ b/testsuite/openshift/config.py @@ -0,0 +1,188 @@ +"""Module containing all config classes""" +from abc import ABC, abstractmethod +from enum import Enum +from functools import cached_property + +import openshift as oc +import yaml + +from testsuite.openshift import OpenShiftObject +from testsuite.openshift.client import OpenShiftClient + + +def convert_to_yaml(data: list[str | dict]): + """Convert dict to specific format Marin3r uses and convert value to yaml if it is not a string""" + transformed = [] + for value in data: + transformed.append({"value": value if isinstance(value, str) else yaml.dump(value)}) + return transformed + + +class BaseEnvoyConfig(OpenShiftObject, ABC): + """Base class for all EnvoyConfigs""" + + class Status(Enum): + """All known statuses of EnvoyConfig""" + + # pylint: disable=invalid-name + InSync = "InSync" + Rollback = "Rollback" + + @classmethod + @abstractmethod + def create_instance( + cls, + openshift: OpenShiftClient, + name, + listeners, + clusters=None, + endpoints=None, + runtimes=None, + routes=None, + scoped_routes=None, + secrets=None, + labels=None, + ): + """Creates new EnvoyConfig instance""" + + @property + @abstractmethod + def listeners(self): + """Returns all configured listeners""" + + @cached_property + def ports(self) -> dict[str, int]: + """Returns all the configured ports and their name""" + ports = {} + for listener in self.listeners: + ports[listener["name"]] = listener["address"]["socket_address"]["port_value"] + return ports + + def wait_status(self, status: Status, timeout=30): + """Waits until config has the expected status""" + with oc.timeout(timeout): + + def _status(obj): + return obj.model.status.cacheState == status.value + + success, _, _ = self.self_selector().until_all(success_func=_status) + return success + + +class LegacyEnvoyConfig(BaseEnvoyConfig): + """Legacy EnvoyConfig resource, using envoyResources field""" + + @classmethod + def create_instance( + cls, + openshift: OpenShiftClient, + name, + listeners, + clusters=None, + endpoints=None, + runtimes=None, + routes=None, + scoped_routes=None, + secrets=None, + labels=None, + ): + """Creates new EnvoyConfig""" + model = { + "apiVersion": "marin3r.3scale.net/v1alpha1", + "kind": "EnvoyConfig", + "metadata": {"name": name}, + "spec": { + "nodeID": name, + "serialization": "yaml", + "envoyResources": { + "clusters": convert_to_yaml(clusters or []), + "endpoints": convert_to_yaml(endpoints or []), + "runtimes": convert_to_yaml(runtimes or []), + "routes": convert_to_yaml(routes or []), + "scopedRoutes": convert_to_yaml(scoped_routes or []), + "listeners": convert_to_yaml(listeners), + "secrets": [{"name": value} for value, _ in (secrets or [])], + }, + }, + } + + if labels is not None: + model["metadata"]["labels"] = labels + + return cls(model, context=openshift.context) + + @property + def listeners(self): + listeners = [] + for listener in self.model.spec.envoyResources.listeners: + listeners.append(yaml.safe_load(listener["value"])) + return listeners + + +class EnvoyConfig(BaseEnvoyConfig): + """Envoy config configured using spec.resources""" + + # pylint: disable=too-many-locals + @classmethod + def create_instance( + cls, + openshift: OpenShiftClient, + name, + listeners, + clusters=None, + endpoints=None, + runtimes=None, + routes=None, + scoped_routes=None, + secrets=None, + labels=None, + ): + """Creates new instance""" + model = { + "apiVersion": "marin3r.3scale.net/v1alpha1", + "kind": "EnvoyConfig", + "metadata": {"name": name}, + "spec": { + "nodeID": name, + "serialization": "yaml", + "resources": [], + }, + } + for resource_type, values in { + "cluster": clusters, + "endpoint": endpoints, + "runtime": runtimes, + "route": routes, + "scopedRoute": scoped_routes, + "listener": listeners, + }.items(): + for value in values or []: + if isinstance(value, str): + value = yaml.safe_load(value) + model["spec"]["resources"].append( + { + "type": resource_type, + "value": value, + } + ) + for secret, is_ca in secrets or []: + resource = { + "type": "secret", + "generateFromTlsSecret": secret, + } + if is_ca: + resource["blueprint"] = "validationContext" + model["spec"]["resources"].append(resource) + + if labels is not None: + model["metadata"]["labels"] = labels + + return cls(model, context=openshift.context) + + @property + def listeners(self): + sections = [] + for section in self.model.spec.resources: + if section.type == "listener": + sections.append(section["value"]) + return sections diff --git a/testsuite/openshift/envoy.py b/testsuite/openshift/envoy.py index 61eebe3..e134009 100644 --- a/testsuite/openshift/envoy.py +++ b/testsuite/openshift/envoy.py @@ -1,13 +1,11 @@ """Module containing all classes related to Envoy configured by Marin3r""" -from enum import Enum -from functools import cached_property -import yaml import openshift as oc from testsuite.httpx import HttpxBackoffClient from testsuite.openshift import OpenShiftObject, LifecycleObject from testsuite.openshift.client import OpenShiftClient +from testsuite.openshift.config import LegacyEnvoyConfig from testsuite.openshift.httpbin import Httpbin from testsuite.openshift.route import Route @@ -30,82 +28,6 @@ def create_instance(cls, openshift: OpenShiftClient, name, labels=None): return cls(model, context=openshift.context) -def convert_to_yaml(data: dict[str, dict]): - """Convert dict to specific format Marin3r uses and convert value to yaml if it is not a string""" - transformed = [] - for key, value in data.items(): - transformed.append({"name": key, "value": value if isinstance(value, str) else yaml.dump(value)}) - return transformed - - -class EnvoyConfig(OpenShiftObject): - """EnvoyConfig resource""" - - # pylint: disable=invalid-name - class Status(Enum): - """All known statuses of EnvoyConfig""" - - InSync = "InSync" - Rollback = "Rollback" - - @classmethod - def create_instance( - cls, - openshift: OpenShiftClient, - name, - listeners, - clusters=None, - endpoints=None, - runtimes=None, - routes=None, - scoped_routes=None, - secrets=None, - labels=None, - ): - """Creates new EnvoyConfig""" - model = { - "apiVersion": "marin3r.3scale.net/v1alpha1", - "kind": "EnvoyConfig", - "metadata": {"name": name}, - "spec": { - "nodeID": name, - "serialization": "yaml", - "envoyResources": { - "clusters": convert_to_yaml(clusters or {}), - "endpoints": convert_to_yaml(endpoints or {}), - "runtimes": convert_to_yaml(runtimes or {}), - "routes": convert_to_yaml(routes or {}), - "scoped_routes": convert_to_yaml(scoped_routes or {}), - "listeners": convert_to_yaml(listeners), - "secrets": [{"name": value} for value in (secrets or {}).values()], - }, - }, - } - - if labels is not None: - model["metadata"]["labels"] = labels - - return cls(model, context=openshift.context) - - @cached_property - def ports(self) -> dict[str, int]: - """Returns all the configured ports and their name""" - ports = {} - for listener in self.model.spec.envoyResources.listeners: - ports[listener.name] = yaml.safe_load(listener.value)["address"]["socket_address"]["port_value"] - return ports - - def wait_status(self, status: Status, timeout=30): - """Waits until config has the expected status""" - with oc.timeout(timeout): - - def _status(obj): - return obj.model.status.cacheState == status.value - - success, _, _ = self.self_selector().until_all(success_func=_status) - return success - - class EnvoyDeployment(OpenShiftObject): """Envoy deployed from template""" @@ -115,7 +37,7 @@ def create_instance( openshift: OpenShiftClient, name, discovery_service: DiscoveryService, - config: EnvoyConfig, + config: LegacyEnvoyConfig, image, labels=None, ): @@ -155,7 +77,7 @@ def __init__( openshift: OpenShiftClient, name, discovery_service: DiscoveryService, - config: EnvoyConfig, + config: LegacyEnvoyConfig, backend: Httpbin, image, tls=False, diff --git a/testsuite/openshift/httpbin.py b/testsuite/openshift/httpbin.py index 13fb98b..10dbe8a 100644 --- a/testsuite/openshift/httpbin.py +++ b/testsuite/openshift/httpbin.py @@ -23,8 +23,10 @@ def url(self): return f"{self.name}.{self.openshift.project}.svc.cluster.local" def commit(self): - with resources.path("testsuite.resources", "httpbin.yaml") as path: - self.httpbin_objects = self.openshift.new_app(path, {"NAME": self.name, "LABEL": self.label}) + self.httpbin_objects = self.openshift.new_app( + resources.files("testsuite.resources").joinpath("httpbin.yaml"), + {"NAME": self.name, "LABEL": self.label}, + ) with self.openshift.context: assert self.openshift.is_ready(self.httpbin_objects.narrow("deployment")), "Httpbin wasn't ready in time" diff --git a/testsuite/tests/conftest.py b/testsuite/tests/conftest.py index d585c18..e452226 100644 --- a/testsuite/tests/conftest.py +++ b/testsuite/tests/conftest.py @@ -2,7 +2,8 @@ import pytest from testsuite.config import settings -from testsuite.openshift.envoy import DiscoveryService, EnvoyConfig, Envoy, SidecarEnvoy +from testsuite.openshift.envoy import DiscoveryService, Envoy, SidecarEnvoy +from testsuite.openshift.config import LegacyEnvoyConfig, EnvoyConfig from testsuite.openshift.httpbin import Httpbin from testsuite.utils import randomize, _whoami, create_simple_cluster @@ -78,8 +79,8 @@ def discovery_service(request, openshift, blame, label): @pytest.fixture(scope="module") def listeners(): """Listeners section of EnvoyConfig. Keys are name, value is config""" - return { - "http": """ + return [ + """ name: http address: socket_address: @@ -104,52 +105,69 @@ def listeners(): typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router """ - } + ] @pytest.fixture(scope="module") def clusters(backend): """Clusters section of EnvoyConfig. Keys are name, value is config""" - return {"httpbin": create_simple_cluster(backend, "httpbin")} + return [create_simple_cluster(backend, "httpbin")] @pytest.fixture(scope="module") def endpoints(): """Endpoints section of EnvoyConfig. Keys are name, value is config""" - return {} + return [] @pytest.fixture(scope="module") def runtimes(): """Runtimes section of EnvoyConfig. Keys are name, value is config""" - return {} + return [] @pytest.fixture(scope="module") def routes(): """Routes section of EnvoyConfig. Keys are name, value is config""" - return {} + return [] @pytest.fixture(scope="module") def scoped_routes(): """ScopedRoutes section of EnvoyConfig. Keys are name, value is config""" - return {} + return [] @pytest.fixture(scope="module") def secrets(): """Secrets section of EnvoyConfig. Keys are name, value is secret name""" - return {} + return [] + + +@pytest.fixture(scope="module", params=[LegacyEnvoyConfig, EnvoyConfig]) +def envoy_config_class(request): + """Envoy class to use in tests""" + return request.param # pylint: disable=unused-argument @pytest.fixture(scope="module") def envoy_config( - request, openshift, blame, listeners, clusters, endpoints, runtimes, routes, scoped_routes, secrets, envoy_class + request, + openshift, + blame, + listeners, + clusters, + endpoints, + runtimes, + routes, + scoped_routes, + secrets, + envoy_class, + envoy_config_class, ): """EnvoyConfig""" - config = EnvoyConfig.create_instance( + config = envoy_config_class.create_instance( openshift, blame("config"), listeners, @@ -178,7 +196,17 @@ def use_tls(): @pytest.fixture(scope="module") -def envoy(request, envoy_class, openshift, envoy_config, discovery_service, blame, testconfig, backend, use_tls): +def envoy( + request, + envoy_class, + openshift, + envoy_config, + discovery_service, + blame, + testconfig, + backend, + use_tls, +): """Envoy to be used in tests""" envoy = envoy_class( openshift, diff --git a/testsuite/tests/test_reject.py b/testsuite/tests/test_reject.py index b4a214c..eb017a9 100644 --- a/testsuite/tests/test_reject.py +++ b/testsuite/tests/test_reject.py @@ -2,24 +2,22 @@ import pytest from openshift import OpenShiftPythonException -from testsuite.openshift.envoy import EnvoyConfig - -def test_reject_invalid_config(openshift, blame): +def test_reject_invalid_config(openshift, blame, envoy_config_class): """Invalid configuration should be rejected by a webhook""" - config = EnvoyConfig.create_instance( + config = envoy_config_class.create_instance( openshift, blame("config"), - { - "http": """ + [ + """ name: http enable_reuse_port: false address: MISSING """ - }, + ], ) with pytest.raises(OpenShiftPythonException) as exc: config.commit() - assert 'admission webhook "envoyconfig.marin3r.3scale.net" denied the request' in exc.value.result.err() + assert 'admission webhook "envoyconfig.marin3r.3scale.net-v1alpha1" denied the request' in exc.value.result.err() diff --git a/testsuite/tests/test_rollback.py b/testsuite/tests/test_rollback.py index 1b1e30a..7d49e6b 100644 --- a/testsuite/tests/test_rollback.py +++ b/testsuite/tests/test_rollback.py @@ -1,8 +1,9 @@ """Tests if rollback functionality works as expected, e.g. rejected configuration should be rolled back https://github.com/3scale-ops/marin3r/blob/main/docs/walkthroughs/self-healing.md """ -from testsuite.openshift.envoy import EnvoyConfig +import yaml +from testsuite.openshift.config import BaseEnvoyConfig, LegacyEnvoyConfig # You cannot change socket_options with envoy 1.25, and the update should be rejected INVALID_LISTENER_CHANGE = """ @@ -39,23 +40,32 @@ """ -def update_config(config: EnvoyConfig): +def update_config(config: BaseEnvoyConfig): """Updates EnvoyConfig configuration to an invalid one""" - def _apply(obj): - obj.model.spec.envoyResources.listeners = [{"name": "http", "value": INVALID_LISTENER_CHANGE}] + if isinstance(config, LegacyEnvoyConfig): + + def _apply(obj): + obj.model.spec.envoyResources.listeners = [{"value": INVALID_LISTENER_CHANGE}] + + else: + + def _apply(obj): + for resource in obj.model.spec.resources: + if resource.type == "listener": + resource.value = yaml.safe_load(INVALID_LISTENER_CHANGE) result, success = config.modify_and_apply(_apply) assert success, f"Config wasn't updated: {result}" -def test_update_config(client, envoy_config): +def test_rollback(client, envoy_config): """Tests if the incorrect configuration will be rolled back and won't stop working""" response = client.get("/get") assert response.status_code == 200 update_config(envoy_config) - assert envoy_config.wait_status(EnvoyConfig.Status.Rollback) + assert envoy_config.wait_status(BaseEnvoyConfig.Status.Rollback) response = client.get("/get") assert response.status_code == 200 diff --git a/testsuite/tests/test_routes.py b/testsuite/tests/test_routes.py index 4219b15..f2e4483 100644 --- a/testsuite/tests/test_routes.py +++ b/testsuite/tests/test_routes.py @@ -5,8 +5,8 @@ @pytest.fixture(scope="module") def routes(): """Routes configuration for httpbin""" - return { - "local": """ + return [ + """ name: local virtual_hosts: - name: all @@ -17,14 +17,14 @@ def routes(): route: cluster: httpbin """ - } + ] @pytest.fixture(scope="module") def listeners(): """Listeners section of EnvoyConfig. Keys are name, value is config""" - return { - "http": """ + return [ + """ name: http address: socket_address: @@ -43,7 +43,7 @@ def listeners(): typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router """ - } + ] def test_routes(client): diff --git a/testsuite/tests/test_update_config.py b/testsuite/tests/test_update_config.py index 2cd663a..f1da4a0 100644 --- a/testsuite/tests/test_update_config.py +++ b/testsuite/tests/test_update_config.py @@ -1,42 +1,48 @@ """Tests if new configuration is applied, if valid""" import yaml -from testsuite.openshift.envoy import EnvoyConfig - - -INVALID_URL_CLUSTER = yaml.dump( - { - "name": "httpbin", - "connect_timeout": "0.25s", - "type": "STRICT_DNS", - "load_assignment": { - "cluster_name": "httpbin", - "endpoints": [ - { - "lb_endpoints": [ - { - "endpoint": { - "address": { - "socket_address": { - "address": "invalid.service", - "port_value": 8080, - } +from testsuite.openshift.config import BaseEnvoyConfig, LegacyEnvoyConfig + +INVALID_URL_CLUSTER = { + "name": "httpbin", + "connect_timeout": "0.25s", + "type": "STRICT_DNS", + "load_assignment": { + "cluster_name": "httpbin", + "endpoints": [ + { + "lb_endpoints": [ + { + "endpoint": { + "address": { + "socket_address": { + "address": "invalid.service", + "port_value": 8080, } } } - ] - } - ], - }, - } -) + } + ] + } + ], + }, +} -def update_config(config: EnvoyConfig): +def update_config(config: BaseEnvoyConfig): """Update envoy configuration""" - def _apply(obj): - obj.model.spec.envoyResources.clusters = [{"name": "invalid_httpbin", "value": INVALID_URL_CLUSTER}] + if isinstance(config, LegacyEnvoyConfig): + + def _apply(obj): + obj.model.spec.envoyResources.clusters = [{"value": yaml.dump(INVALID_URL_CLUSTER)}] + + else: + + def _apply(obj): + for resource in obj.model.spec.resources: + if resource.type == "cluster": + resource.value = INVALID_URL_CLUSTER result, success = config.modify_and_apply(_apply) assert success, f"Config wasn't updated: {result}" @@ -48,7 +54,7 @@ def test_update_config(client, envoy_config): assert response.status_code == 200 update_config(envoy_config) - assert envoy_config.wait_status(EnvoyConfig.Status.InSync) + assert envoy_config.wait_status(BaseEnvoyConfig.Status.InSync) client.retry_codes = {} response = client.get("/get") diff --git a/testsuite/tests/tls/conftest.py b/testsuite/tests/tls/conftest.py index 13add29..29b8926 100644 --- a/testsuite/tests/tls/conftest.py +++ b/testsuite/tests/tls/conftest.py @@ -4,14 +4,22 @@ import pytest from testsuite.certificates import CertInfo, Certificate, CFSSLClient +from testsuite.openshift.config import LegacyEnvoyConfig from testsuite.utils import cert_builder +@pytest.fixture(scope="module", autouse=True) +def skip_for_legacy(envoy_config_class): + """Skip if using old configuration""" + if envoy_config_class == LegacyEnvoyConfig: + pytest.skip("TLS tests do not work with envoyResources") + + @pytest.fixture(scope="module") -def listeners(secrets): +def listeners(envoy_ca, envoy_cert): """Listeners section of EnvoyConfig. Keys are name, value is config""" - return { - "http": f""" + return [ + f""" name: http address: socket_address: @@ -25,10 +33,10 @@ def listeners(secrets): require_client_certificate: true common_tls_context: tls_certificate_sds_secret_configs: - - name: {secrets["envoy_cert"]} + - name: {envoy_cert} sds_config: {{ ads: {{}}, resource_api_version: "V3" }} validation_context_sds_secret_config: - name: {secrets["envoy_ca"]} + name: {envoy_ca} sds_config: {{ ads: {{}}, resource_api_version: "V3" }} filters: - name: envoy.http_connection_manager @@ -48,15 +56,25 @@ def listeners(secrets): typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router """ - } + ] + + +@pytest.fixture(scope="module") +def envoy_ca(create_secret, certificates, blame): + """Envoy Certificate Authority""" + return create_secret(certificates["envoy_ca"], blame("envoy-ca")) + + +@pytest.fixture(scope="module") +def envoy_cert(create_secret, certificates, blame): + """Envoy Certificate""" + return create_secret(certificates["envoy_cert"], blame("envoy-cert")) @pytest.fixture(scope="module") -def secrets(create_secret, certificates, blame): +def secrets(envoy_cert, envoy_ca): """Define all the secrets used in TLS, specifically CA and envoy cert""" - envoy_cert = create_secret(certificates["envoy_cert"], blame("envoy-cert")) - envoy_ca = create_secret(certificates["envoy_ca"], blame("envoy-ca")) - return {"envoy_cert": envoy_cert, "envoy_ca": envoy_ca} + return [(envoy_cert, False), (envoy_ca, True)] @pytest.fixture(scope="session") diff --git a/testsuite/tests/tls/test_tls.py b/testsuite/tests/tls/test_tls.py index 0016d7d..78da4c8 100644 --- a/testsuite/tests/tls/test_tls.py +++ b/testsuite/tests/tls/test_tls.py @@ -10,8 +10,6 @@ def test_valid_certificate(certificates, envoy): assert response.status_code == 200 -# https://github.com/3scale-ops/marin3r/issues/169 -@pytest.mark.xfail def test_no_certificate(envoy, certificates): """Test that request without certificate will be rejected""" with pytest.raises(ReadError, match="certificate required"): @@ -19,8 +17,6 @@ def test_no_certificate(envoy, certificates): client.get("/get") -# https://github.com/3scale-ops/marin3r/issues/169 -@pytest.mark.xfail def test_invalid_certificate(certificates, envoy): """Tests that certificate with different CA will be rejeceted""" with pytest.raises(ReadError, match="unknown ca"):