From 0b7244cfd32bf45284afa6979830b89cba2f40c6 Mon Sep 17 00:00:00 2001 From: phala Date: Mon, 29 Apr 2024 13:14:07 +0200 Subject: [PATCH 1/2] Create Policy object --- testsuite/policy/__init__.py | 19 +++++++++++++++++++ testsuite/policy/authorization/auth_policy.py | 19 ++++--------------- testsuite/policy/dns_policy.py | 16 ++-------------- testsuite/policy/rate_limit_policy.py | 13 +++++++------ testsuite/policy/tls_policy.py | 4 ++-- 5 files changed, 34 insertions(+), 37 deletions(-) diff --git a/testsuite/policy/__init__.py b/testsuite/policy/__init__.py index e69de29b..49786898 100644 --- a/testsuite/policy/__init__.py +++ b/testsuite/policy/__init__.py @@ -0,0 +1,19 @@ +"""Contains Base class for policies""" + +import openshift_client as oc + +from testsuite.openshift import OpenShiftObject +from testsuite.utils import has_condition + + +class Policy(OpenShiftObject): + """Base class with common functionality for all policies""" + + def wait_for_ready(self): + """Wait for a Policy to be Enforced""" + with oc.timeout(90): + success, _, _ = self.self_selector().until_all( + success_func=has_condition("Enforced", "True"), + tolerate_failures=5, + ) + assert success, f"{self.kind()} did not get ready in time" diff --git a/testsuite/policy/authorization/auth_policy.py b/testsuite/policy/authorization/auth_policy.py index 13c8b5ae..33cf7a7e 100644 --- a/testsuite/policy/authorization/auth_policy.py +++ b/testsuite/policy/authorization/auth_policy.py @@ -2,19 +2,18 @@ from typing import Dict, TYPE_CHECKING -import openshift_client as oc - -from testsuite.utils import asdict, has_condition from testsuite.gateway import Referencable -from testsuite.openshift.client import OpenShiftClient from testsuite.openshift import modify +from testsuite.openshift.client import OpenShiftClient +from testsuite.utils import asdict from .auth_config import AuthConfig +from .. import Policy if TYPE_CHECKING: from . import Rule -class AuthPolicy(AuthConfig): +class AuthPolicy(AuthConfig, Policy): """AuthPolicy object, it serves as Kuadrants AuthConfig""" @property @@ -47,13 +46,3 @@ def add_rule(self, when: list["Rule"]): """Add rule for the skip of entire AuthPolicy""" self.model.spec.setdefault("when", []) self.model.spec["when"].extend([asdict(x) for x in when]) - - def wait_for_ready(self): - """Waits until AuthPolicy object reports ready status""" - with oc.timeout(90): - success, _, _ = self.self_selector().until_all( - success_func=has_condition("Enforced", "True"), - tolerate_failures=5, - ) - assert success, f"{self.kind()} did not get ready in time" - self.refresh() diff --git a/testsuite/policy/dns_policy.py b/testsuite/policy/dns_policy.py index 50252cac..2330de9f 100644 --- a/testsuite/policy/dns_policy.py +++ b/testsuite/policy/dns_policy.py @@ -1,14 +1,11 @@ """Module for DNSPolicy related classes""" -import openshift_client as oc - from testsuite.gateway import Referencable -from testsuite.openshift import OpenShiftObject from testsuite.openshift.client import OpenShiftClient -from testsuite.utils import has_condition +from testsuite.policy import Policy -class DNSPolicy(OpenShiftObject): +class DNSPolicy(Policy): """DNSPolicy object""" @classmethod @@ -29,12 +26,3 @@ def create_instance( } return cls(model, context=openshift.context) - - def wait_for_ready(self): - """Wait for DNSPolicy to be Enforced""" - with oc.timeout(90): - success, _, _ = self.self_selector().until_all( - success_func=has_condition("Enforced", "True"), - tolerate_failures=5, - ) - assert success, f"{self.kind()} did not get ready in time" diff --git a/testsuite/policy/rate_limit_policy.py b/testsuite/policy/rate_limit_policy.py index af986bd7..37abaa47 100644 --- a/testsuite/policy/rate_limit_policy.py +++ b/testsuite/policy/rate_limit_policy.py @@ -4,13 +4,14 @@ from dataclasses import dataclass from typing import Iterable, Literal, Optional, List -import openshift_client as oc +from openshift_client import timeout -from testsuite.policy.authorization import Rule -from testsuite.utils import asdict, has_condition from testsuite.gateway import Referencable, RouteMatch +from testsuite.openshift import modify from testsuite.openshift.client import OpenShiftClient -from testsuite.openshift import OpenShiftObject, modify +from testsuite.policy import Policy +from testsuite.policy.authorization import Rule +from testsuite.utils import asdict, has_condition @dataclass @@ -39,7 +40,7 @@ def __init__(self, *matches: RouteMatch, hostnames: Optional[List[str]] = None): self.hostnames = hostnames -class RateLimitPolicy(OpenShiftObject): +class RateLimitPolicy(Policy): """RateLimitPolicy (or RLP for short) object, used for applying rate limiting rules to a Gateway/HTTPRoute""" @classmethod @@ -80,7 +81,7 @@ def add_limit( def wait_for_ready(self): """Wait for RLP to be enforced""" - with oc.timeout(90): + with timeout(90): success, _, _ = self.self_selector().until_all( success_func=has_condition("Enforced", "True"), tolerate_failures=5, diff --git a/testsuite/policy/tls_policy.py b/testsuite/policy/tls_policy.py index 1db4b720..e72b3e41 100644 --- a/testsuite/policy/tls_policy.py +++ b/testsuite/policy/tls_policy.py @@ -4,11 +4,11 @@ from testsuite.gateway import Referencable from testsuite.openshift.client import OpenShiftClient -from testsuite.openshift import OpenShiftObject +from testsuite.policy import Policy from testsuite.utils import has_condition -class TLSPolicy(OpenShiftObject): +class TLSPolicy(Policy): """TLSPolicy object""" @classmethod From 0ceeb2667a2dce18adc2b45d6f097bbf3532c117 Mon Sep 17 00:00:00 2001 From: phala Date: Mon, 29 Apr 2024 15:08:12 +0200 Subject: [PATCH 2/2] Add AffectedBy status test --- testsuite/gateway/gateway_api/gateway.py | 15 +++++++++ testsuite/gateway/gateway_api/route.py | 18 ++++++++++- .../reconciliation/test_affected_by.py | 31 +++++++++++++++++++ .../reconciliation/test_gw_doesnt_exist.py | 5 ++- testsuite/utils.py | 19 ++++++++---- 5 files changed, 78 insertions(+), 10 deletions(-) create mode 100644 testsuite/tests/kuadrant/gateway/reconciliation/test_affected_by.py diff --git a/testsuite/gateway/gateway_api/gateway.py b/testsuite/gateway/gateway_api/gateway.py index d78d1fe3..12551c11 100644 --- a/testsuite/gateway/gateway_api/gateway.py +++ b/testsuite/gateway/gateway_api/gateway.py @@ -8,6 +8,8 @@ from testsuite.gateway import Gateway from testsuite.openshift.client import OpenShiftClient from testsuite.openshift import OpenShiftObject +from testsuite.policy import Policy +from testsuite.utils import check_condition class KuadrantGateway(OpenShiftObject, Gateway): @@ -77,6 +79,19 @@ def wait_for_ready(self, timeout: int = 180): assert success, "Gateway didn't get ready in time" self.refresh() + def is_affected_by(self, policy: Policy) -> bool: + """Returns True, if affected by status is found within the object for the specific policy""" + for condition in self.model.status.conditions: + if check_condition( + condition, + f"kuadrant.io/{policy.kind(lowercase=False)}Affected", + "True", + "Accepted", + f"Object affected by {policy.kind(lowercase=False)} {policy.namespace()}/{policy.name()}", + ): + return True + return False + def get_tls_cert(self): if "tls" not in self.model.spec.listeners[0]: return None diff --git a/testsuite/gateway/gateway_api/route.py b/testsuite/gateway/gateway_api/route.py index 42e5e88f..902e5d9c 100644 --- a/testsuite/gateway/gateway_api/route.py +++ b/testsuite/gateway/gateway_api/route.py @@ -8,7 +8,8 @@ from testsuite.gateway import Gateway, GatewayRoute, PathMatch, MatchType, RouteMatch from testsuite.openshift.client import OpenShiftClient from testsuite.openshift import OpenShiftObject, modify -from testsuite.utils import asdict +from testsuite.policy import Policy +from testsuite.utils import asdict, check_condition if typing.TYPE_CHECKING: from testsuite.backend import Backend @@ -43,6 +44,21 @@ def create_instance( return cls(model, context=openshift.context) + def is_affected_by(self, policy: Policy): + """Returns True, if affected by status is found within the object for the specific policy""" + for condition_set in self.model.status.parents: + if condition_set.controllerName == "kuadrant.io/policy-controller": + for condition in condition_set.conditions: + if check_condition( + condition, + f"kuadrant.io/{policy.kind(lowercase=False)}Affected", + "True", + "Accepted", + f"Object affected by {policy.kind(lowercase=False)} {policy.namespace()}/{policy.name()}", + ): + return True + return False + @property def reference(self): return { diff --git a/testsuite/tests/kuadrant/gateway/reconciliation/test_affected_by.py b/testsuite/tests/kuadrant/gateway/reconciliation/test_affected_by.py new file mode 100644 index 00000000..339cd887 --- /dev/null +++ b/testsuite/tests/kuadrant/gateway/reconciliation/test_affected_by.py @@ -0,0 +1,31 @@ +"""Tests that affected by status is applied correctly to the HTTPRoute and Gateway""" + +import pytest + +pytestmark = [pytest.mark.kuadrant_only] + + +def test_route_status(route, rate_limit, authorization): + """Tests affected by status for HTTPRoute""" + route.refresh() + assert route.is_affected_by(rate_limit) + assert route.is_affected_by(authorization) + + rate_limit.delete() + assert not route.wait_until(lambda obj: obj.is_affected_by(rate_limit)) + + authorization.delete() + assert not route.wait_until(lambda obj: obj.is_affected_by(authorization)) + + +def test_gateway_status(gateway, dns_policy, tls_policy): + """Tests affected by status for Gateway""" + gateway.refresh() + assert gateway.is_affected_by(dns_policy) + assert gateway.is_affected_by(tls_policy) + + dns_policy.delete() + assert not gateway.wait_until(lambda obj: obj.is_affected_by(dns_policy)) + + tls_policy.delete() + assert not gateway.wait_until(lambda obj: obj.is_affected_by(tls_policy)) diff --git a/testsuite/tests/kuadrant/gateway/reconciliation/test_gw_doesnt_exist.py b/testsuite/tests/kuadrant/gateway/reconciliation/test_gw_doesnt_exist.py index 72d7ca85..8f1e373f 100644 --- a/testsuite/tests/kuadrant/gateway/reconciliation/test_gw_doesnt_exist.py +++ b/testsuite/tests/kuadrant/gateway/reconciliation/test_gw_doesnt_exist.py @@ -4,7 +4,6 @@ from testsuite.gateway import CustomReference from testsuite.policy.tls_policy import TLSPolicy -from testsuite.utils import has_condition from . import dns_policy pytestmark = [pytest.mark.kuadrant_only] @@ -33,6 +32,6 @@ def test_no_gw(request, create_cr, hub_openshift, blame, module_label, cluster_i request.addfinalizer(policy.delete) policy.commit() - assert policy.wait_until( - has_condition("Accepted", "False", "TargetNotFound", "target does-not-exist was not found"), timelimit=20 + assert policy.wait_for_condition( + "Accepted", "False", "TargetNotFound", "target does-not-exist was not found", timelimit=20 ), f"Policy did not reach expected status, instead it was: {policy.refresh().model.status.conditions}" diff --git a/testsuite/utils.py b/testsuite/utils.py index 694bd16b..df2113e0 100644 --- a/testsuite/utils.py +++ b/testsuite/utils.py @@ -170,17 +170,24 @@ def _asdict_recurse(obj): return result +def check_condition(condition, condition_type, status, reason=None, message=None): + """Checks if condition matches expectation, won't check message and reason if they are None""" + if ( # pylint: disable=too-many-boolean-expressions + condition.type == condition_type + and condition.status == status + and (message is None or message in condition.message) + and (reason is None or reason == condition.reason) + ): + return True + return False + + def has_condition(condition_type, status="True", reason=None, message=None): """Returns function, that returns True if the Kubernetes object has a specific value""" def _check(obj): for condition in obj.model.status.conditions: - if ( # pylint: disable=too-many-boolean-expressions - condition.type == condition_type - and condition.status == status - and (message is None or message in condition.message) - and (reason is None or reason == condition.reason) - ): + if check_condition(condition, condition_type, status, reason, message): return True return False