Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

D&O merged policies / sectionName tests #608

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
8 changes: 8 additions & 0 deletions testsuite/kuadrant/policy/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
"""Contains Base class for policies"""

from dataclasses import dataclass
from enum import Enum

from testsuite.kubernetes import KubernetesObject
from testsuite.utils import check_condition


class Strategy(Enum):
"""Class for merge strategies of defaults and overrides."""

ATOMIC = "atomic"
MERGE = "merge"


@dataclass
class CelPredicate:
"""Dataclass that references CEL predicate e.g. auth.identity.anonymous == 'true'"""
Expand Down
23 changes: 21 additions & 2 deletions testsuite/kuadrant/policy/authorization/auth_policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@
from testsuite.kubernetes import modify
from testsuite.kubernetes.client import KubernetesClient
from testsuite.utils import asdict

from .. import CelPredicate, Policy, Strategy
from . import Pattern
from .auth_config import AuthConfig
from .sections import ResponseSection
from .. import Policy, CelPredicate
from . import Pattern


class AuthPolicy(Policy, AuthConfig):
Expand All @@ -27,6 +28,7 @@ def create_instance(
name,
target: Referencable,
labels: Dict[str, str] = None,
section_name: str = None,
):
"""Creates base instance"""
model: Dict = {
Expand All @@ -37,6 +39,8 @@ def create_instance(
"targetRef": target.reference,
},
}
if section_name:
model["spec"]["targetRef"]["sectionName"] = section_name

return cls(model, context=cluster.context)

Expand All @@ -46,6 +50,21 @@ def add_rule(self, when: list[CelPredicate]):
self.model.spec.setdefault("when", [])
self.model.spec["when"].extend([asdict(x) for x in when])

# TODO: need to check if the typing is set up correctlly.
@modify
def strategy(self, strategy: Strategy) -> None:
"""Add strategy type to default or overrides spec"""
if self.spec_section is None:
if "defaults" in self.model.spec:
self.spec_section = self.model.spec["default"]
elif "overrides" in self.model.spec:
self.spec_section = self.model.spec["overrides"]
else:
raise TypeError("no default or override section found in spec")

self.spec_section["strategy"] = strategy.value
self.spec_section = None

@property
def auth_section(self):
if self.spec_section is None:
Expand Down
3 changes: 3 additions & 0 deletions testsuite/kuadrant/policy/dns.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ def create_instance(
name: str,
parent: Referencable,
provider_secret_name: str,
section_name: str = None,
load_balancing: LoadBalancing = None,
labels: dict[str, str] = None,
):
Expand All @@ -86,6 +87,8 @@ def create_instance(

if load_balancing:
model["spec"]["loadBalancing"] = asdict(load_balancing)
if section_name:
model["spec"]["targetRef"]["sectionName"] = section_name

return cls(model, context=cluster.context)

Expand Down
30 changes: 27 additions & 3 deletions testsuite/kuadrant/policy/rate_limit.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
from typing import Iterable

from testsuite.gateway import Referencable
from testsuite.kuadrant.policy import CelExpression, CelPredicate, Policy, Strategy
from testsuite.kubernetes import modify
from testsuite.kubernetes.client import KubernetesClient
from testsuite.kuadrant.policy import Policy, CelPredicate, CelExpression
from testsuite.utils import asdict


Expand All @@ -27,16 +27,25 @@ def __init__(self, *args, **kwargs):
self.spec_section = None

@classmethod
def create_instance(cls, cluster: KubernetesClient, name, target: Referencable, labels: dict[str, str] = None):
def create_instance(
cls,
cluster: KubernetesClient,
name,
target: Referencable,
section_name: str = None,
labels: dict[str, str] = None,
):
"""Creates new instance of RateLimitPolicy"""
model = {
model: dict = {
"apiVersion": "kuadrant.io/v1",
"kind": "RateLimitPolicy",
"metadata": {"name": name, "labels": labels},
"spec": {
"targetRef": target.reference,
},
}
if section_name:
model["spec"]["targetRef"]["sectionName"] = section_name

return cls(model, context=cluster.context)

Expand All @@ -63,6 +72,21 @@ def add_limit(
self.spec_section.setdefault("limits", {})[name] = limit
self.spec_section = None

# TODO: need to check if the typing is set up correctlly.
@modify
def strategy(self, strategy: Strategy) -> None:
"""Add strategy type to default or overrides spec"""
if self.spec_section is None:
if "defaults" in self.model.spec:
self.spec_section = self.model.spec["defaults"]
elif "overrides" in self.model.spec:
self.spec_section = self.model.spec["overrides"]
else:
raise TypeError("no default or override section found in spec")

self.spec_section["strategy"] = strategy.value
self.spec_section = None

@property
def defaults(self):
"""Add new rule into the `defaults` RateLimitPolicy section"""
Expand Down
5 changes: 4 additions & 1 deletion testsuite/kuadrant/policy/tls.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ def create_instance(
name: str,
parent: Referencable,
issuer: Referencable,
section_name: str = None,
labels: dict[str, str] = None,
commonName: str = None,
duration: str = None,
Expand All @@ -24,7 +25,7 @@ def create_instance(
): # pylint: disable=invalid-name
"""Creates new instance of TLSPolicy"""

model = {
model: dict = {
"apiVersion": "kuadrant.io/v1",
"kind": "TLSPolicy",
"metadata": {"name": name, "labels": labels},
Expand All @@ -40,6 +41,8 @@ def create_instance(
},
},
}
if section_name:
model["spec"]["targetRef"]["sectionName"] = section_name

return cls(model, context=cluster.context)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"""Conftest for RLP targeting route tests """
"""Conftest for merge strategy tests"""

import pytest

from testsuite.gateway import PathMatch, RouteMatch, MatchType
from testsuite.gateway import RouteMatch, PathMatch, MatchType


@pytest.fixture(scope="module")
Expand Down
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
"""Setup conftest for policy merge on the same targets"""

import pytest

from testsuite.gateway import MatchType, PathMatch, RouteMatch
from testsuite.kuadrant.policy import CelPredicate, Strategy
from testsuite.kuadrant.policy.rate_limit import Limit, RateLimitPolicy

LIMIT = Limit(8, "10s")
MERGE_LIMIT = Limit(6, "10s")
NO_LIMIT = Limit(10, "10s")


@pytest.fixture(scope="module", autouse=True)
def route(route, backend):
"""Add two new rules to the route"""
route.remove_all_rules()
route.add_rule(
backend,
RouteMatch(path=PathMatch(value="/get", type=MatchType.PATH_PREFIX)),
)
route.add_rule(
backend,
RouteMatch(path=PathMatch(value="/anything", type=MatchType.PATH_PREFIX)),
)
return route


@pytest.fixture(scope="module")
def rate_limit(cluster, blame, module_label, route):
"""Add a RateLimitPolicy targeting the first HTTPRouteRule."""
rate_limit = RateLimitPolicy.create_instance(cluster, blame("sp"), route, labels={"testRun": module_label})
basic_when = [
CelPredicate("request.path == '/get'"),
]
rate_limit.add_limit("basic", [LIMIT], when=basic_when)
return rate_limit


@pytest.fixture(scope="module")
def default_merge_rate_limit(cluster, blame, module_label, route):
"""Add a RateLimitPolicy targeting the first HTTPRouteRule."""
policy = RateLimitPolicy.create_instance(cluster, blame("dmp"), route, labels={"testRun": module_label})
basic_when = [
CelPredicate("request.path == '/get'"),
]
merge_when = [
CelPredicate("request.path == '/anything'"),
]
policy.defaults.add_limit("basic", [MERGE_LIMIT], when=basic_when)
policy.defaults.add_limit("merge", [MERGE_LIMIT], when=merge_when)
policy.strategy(Strategy.MERGE)
return policy
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""Test defaults policy aimed at the same resoure uses oldested policy."""

import pytest

from .conftest import MERGE_LIMIT

pytestmark = [pytest.mark.kuadrant_only]


@pytest.fixture(scope="module", autouse=True)
def commit(request, route, rate_limit, default_merge_rate_limit): # pylint: disable=unused-argument
"""Commits RateLimitPolicy after the HTTPRoute is created"""
for policy in [rate_limit, default_merge_rate_limit]:
request.addfinalizer(policy.delete)
policy.commit()
policy.wait_for_accepted()


@pytest.mark.parametrize("rate_limit", ["gateway", "route"], indirect=True)
def test_multiple_policies_merge_default_ab(client):
"""Test RateLimitPolicy with merge defaults being ingored due to age"""
responses = client.get_many("/get", MERGE_LIMIT.limit)
responses.assert_all(200)
assert client.get("/get").status_code == 429

responses = client.get_many("/anything", MERGE_LIMIT.limit)
responses.assert_all(200)
assert client.get("/anything").status_code == 429
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""Test defaults policy aimed at the same resoure uses oldested policy."""

import pytest

from .conftest import LIMIT, MERGE_LIMIT

pytestmark = [pytest.mark.kuadrant_only]


@pytest.fixture(scope="module", autouse=True)
def commit(request, route, rate_limit, default_merge_rate_limit): # pylint: disable=unused-argument
"""Commits RateLimitPolicy after the HTTPRoute is created"""
for policy in [default_merge_rate_limit, rate_limit]:
request.addfinalizer(policy.delete)
policy.commit()
policy.wait_for_accepted()


@pytest.mark.parametrize("rate_limit", ["gateway", "route"], indirect=True)
def test_multiple_policies_merge_default_ba(client):
"""Test RateLimitPolicy with merge defaults being enforced due to age"""
responses = client.get_many("/get", LIMIT.limit)
responses.assert_all(200)
assert client.get("/get").status_code == 429

responses = client.get_many("/anything", MERGE_LIMIT.limit)
responses.assert_all(200)
assert client.get("/anything").status_code == 429
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""Test gateway level default merging with and being patrially overriden by another policy."""

import pytest

from testsuite.kuadrant.policy import CelPredicate
from testsuite.kuadrant.policy.rate_limit import RateLimitPolicy, Limit, Strategy

pytestmark = [pytest.mark.kuadrant_only]


@pytest.fixture(scope="module")
def rate_limit(rate_limit):
"""Create a RateLimitPolicy with a basic limit with same target as one default."""
when = CelPredicate("request.path == '/get'")
rate_limit.add_limit("route_limit", [Limit(3, "5s")], when=[when])
return rate_limit


@pytest.fixture(scope="module")
def global_rate_limit(cluster, blame, module_label, gateway):
"""Create a RateLimitPolicy with default policies and a merge strategy."""
global_rate_limit = RateLimitPolicy.create_instance(
cluster, blame("limit"), gateway, labels={"testRun": module_label}
)
gateway_when = CelPredicate("request.path == '/anything'")
global_rate_limit.defaults.add_limit("gateway_limit", [Limit(3, "5s")], when=[gateway_when])
route_when = CelPredicate("request.path == '/get'")
global_rate_limit.defaults.add_limit("route_limit", [Limit(10, "5s")], when=[route_when])
global_rate_limit.defaults.strategy(Strategy.MERGE)
return global_rate_limit


@pytest.fixture(scope="module", autouse=True)
def commit(request, route, rate_limit, global_rate_limit): # pylint: disable=unused-argument
"""Commits RateLimitPolicy after the HTTPRoute is created"""
for policy in [global_rate_limit, rate_limit]: # Forcing order of creation.
request.addfinalizer(policy.delete)
policy.commit()
policy.wait_for_ready()


@pytest.mark.parametrize("rate_limit", ["gateway", "route"], indirect=True)
def test_gateway_default_merge(client):
"""Test Gateway default policy being partially overriden when another policy with the same target is created."""
get = client.get_many("/get", 3)
get.assert_all(status_code=200)
assert client.get("/get").status_code == 429

anything = client.get_many("/anything", 3)
anything.assert_all(status_code=200)
assert client.get("/anything").status_code == 429
Empty file.
12 changes: 12 additions & 0 deletions testsuite/tests/singlecluster/defaults/section/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
"""Conftest for RLP section_name targeting tests"""

import pytest


@pytest.fixture(scope="module", autouse=True)
def commit(request, route, rate_limit, authorization): # pylint: disable=unused-argument
"""Commits RateLimitPolicy after the HTTPRoute is created"""
for policy in [authorization, rate_limit]:
request.addfinalizer(policy.delete)
policy.commit()
policy.wait_for_ready()
Loading
Loading