Skip to content

Commit 505aa14

Browse files
authored
add functional tests for rate-limit scaling (nginx#5758)
1 parent a497503 commit 505aa14

File tree

9 files changed

+296
-18
lines changed

9 files changed

+296
-18
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
apiVersion: networking.k8s.io/v1
2+
kind: Ingress
3+
metadata:
4+
annotations:
5+
nginx.org/mergeable-ingress-type: "master"
6+
name: annotations-rl-ingress-master
7+
spec:
8+
ingressClassName: nginx
9+
rules:
10+
- host: ingress-rl.example.com
11+
---
12+
apiVersion: networking.k8s.io/v1
13+
kind: Ingress
14+
metadata:
15+
name: annotations-rl-ingress-minion
16+
annotations:
17+
nginx.org/limit-req-rate: 40r/s
18+
nginx.org/limit-req-key: ${binary_remote_addr}
19+
nginx.org/limit-req-zone-size: 10M
20+
nginx.org/mergeable-ingress-type: "minion"
21+
nginx.org/limit-req-scale: "true"
22+
spec:
23+
ingressClassName: nginx
24+
rules:
25+
- host: ingress-rl.example.com
26+
http:
27+
paths:
28+
- path: /backend1
29+
pathType: Prefix
30+
backend:
31+
service:
32+
name: backend1-svc
33+
port:
34+
number: 80
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
apiVersion: networking.k8s.io/v1
2+
kind: Ingress
3+
metadata:
4+
annotations:
5+
nginx.org/limit-req-rate: 40r/s
6+
nginx.org/limit-req-key: ${binary_remote_addr}
7+
nginx.org/limit-req-zone-size: 10M
8+
nginx.org/limit-req-scale: "true"
9+
name: annotations-rl-ingress
10+
spec:
11+
ingressClassName: nginx
12+
rules:
13+
- host: ingress-rl.example.com
14+
http:
15+
paths:
16+
- path: /backend2
17+
pathType: Prefix
18+
backend:
19+
service:
20+
name: backend2-svc
21+
port:
22+
number: 80
23+
- path: /backend1
24+
pathType: Prefix
25+
backend:
26+
service:
27+
name: backend1-svc
28+
port:
29+
number: 80
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
apiVersion: k8s.nginx.org/v1
2+
kind: Policy
3+
metadata:
4+
name: rate-limit-primary-scaled
5+
spec:
6+
rateLimit:
7+
rate: 40r/s
8+
key: ${binary_remote_addr}
9+
zoneSize: 10M
10+
scale: true
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
apiVersion: k8s.nginx.org/v1
2+
kind: VirtualServerRoute
3+
metadata:
4+
name: backends
5+
spec:
6+
host: virtual-server-route.example.com
7+
upstreams:
8+
- name: backend1
9+
service: backend1-svc
10+
port: 80
11+
- name: backend3
12+
service: backend3-svc
13+
port: 80
14+
subroutes:
15+
- path: "/backends/backend1"
16+
policies:
17+
- name: rate-limit-primary-scaled
18+
action:
19+
pass: backend1
20+
- path: "/backends/backend3"
21+
action:
22+
pass: backend3
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
apiVersion: k8s.nginx.org/v1
2+
kind: VirtualServer
3+
metadata:
4+
name: virtual-server
5+
spec:
6+
host: virtual-server.example.com
7+
policies:
8+
- name: rate-limit-primary-scaled
9+
upstreams:
10+
- name: backend2
11+
service: backend2-svc
12+
port: 80
13+
- name: backend1
14+
service: backend1-svc
15+
port: 80
16+
routes:
17+
- path: "/backend1"
18+
action:
19+
pass: backend1
20+
- path: "/backend2"
21+
action:
22+
pass: backend2

tests/suite/test_rl_ingress.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,10 @@
1414
delete_items_from_yaml,
1515
ensure_connection_to_public_endpoint,
1616
ensure_response_from_backend,
17-
get_events,
1817
get_first_pod_name,
1918
get_ingress_nginx_template_conf,
20-
replace_configmap_from_yaml,
21-
replace_ingress,
19+
get_pod_list,
20+
scale_deployment,
2221
wait_before_test,
2322
wait_until_all_pods_are_ready,
2423
)
@@ -74,6 +73,7 @@ def annotations_setup(
7473

7574
create_example_app(kube_apis, "simple", test_namespace)
7675
wait_until_all_pods_are_ready(kube_apis.v1, test_namespace)
76+
7777
ensure_connection_to_public_endpoint(
7878
ingress_controller_endpoint.public_ip, ingress_controller_endpoint.port, ingress_controller_endpoint.port_ssl
7979
)
@@ -98,7 +98,6 @@ def fin():
9898
)
9999

100100

101-
@pytest.mark.ingresses
102101
@pytest.mark.annotations
103102
@pytest.mark.parametrize("annotations_setup", ["standard", "mergeable"], indirect=True)
104103
class TestRateLimitIngress:
@@ -117,3 +116,27 @@ def test_ingress_rate_limit(self, kube_apis, annotations_setup, ingress_controll
117116
)
118117
counter.append(resp.status_code)
119118
assert (counter.count(200)) <= 2 and (429 in counter) # check for only 2 200s in the list
119+
120+
121+
@pytest.mark.annotations
122+
@pytest.mark.parametrize("annotations_setup", ["standard-scaled", "mergeable-scaled"], indirect=True)
123+
class TestRateLimitIngressScaled:
124+
def test_ingress_rate_limit_sscaled(
125+
self, kube_apis, annotations_setup, ingress_controller_prerequisites, test_namespace
126+
):
127+
"""
128+
Test if rate-limit scaling works with standard and mergeable ingresses
129+
"""
130+
ns = ingress_controller_prerequisites.namespace
131+
scale_deployment(kube_apis.v1, kube_apis.apps_v1_api, "nginx-ingress", ns, 4)
132+
ic_pods = get_pod_list(kube_apis.v1, ns)
133+
for i in range(len(ic_pods)):
134+
conf = get_ingress_nginx_template_conf(
135+
kube_apis.v1,
136+
annotations_setup.namespace,
137+
annotations_setup.ingress_name,
138+
ic_pods[i].metadata.name,
139+
ingress_controller_prerequisites.namespace,
140+
)
141+
flag = ("rate=10r/s" in conf) or ("rate=13r/s" in conf)
142+
assert flag

tests/suite/test_rl_policies.py

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,13 @@
55
from settings import TEST_DATA
66
from suite.utils.custom_resources_utils import read_custom_resource
77
from suite.utils.policy_resources_utils import create_policy_from_yaml, delete_policy
8-
from suite.utils.resources_utils import wait_before_test
8+
from suite.utils.resources_utils import (
9+
get_first_pod_name,
10+
get_pod_list,
11+
get_vs_nginx_template_conf,
12+
scale_deployment,
13+
wait_before_test,
14+
)
915
from suite.utils.vs_vsr_resources_utils import (
1016
create_virtual_server_from_yaml,
1117
delete_virtual_server,
@@ -14,7 +20,9 @@
1420

1521
std_vs_src = f"{TEST_DATA}/rate-limit/standard/virtual-server.yaml"
1622
rl_pol_pri_src = f"{TEST_DATA}/rate-limit/policies/rate-limit-primary.yaml"
23+
rl_pol_pri_sca_src = f"{TEST_DATA}/rate-limit/policies/rate-limit-primary-scaled.yaml"
1724
rl_vs_pri_src = f"{TEST_DATA}/rate-limit/spec/virtual-server-primary.yaml"
25+
rl_vs_pri_sca_src = f"{TEST_DATA}/rate-limit/spec/virtual-server-primary-scaled.yaml"
1826
rl_pol_sec_src = f"{TEST_DATA}/rate-limit/policies/rate-limit-secondary.yaml"
1927
rl_vs_sec_src = f"{TEST_DATA}/rate-limit/spec/virtual-server-secondary.yaml"
2028
rl_pol_invalid = f"{TEST_DATA}/rate-limit/policies/rate-limit-invalid.yaml"
@@ -307,3 +315,51 @@ def test_rl_override_spec_route(
307315
delete_policy(kube_apis.custom_objects, pol_name_sec, test_namespace)
308316
self.restore_default_vs(kube_apis, virtual_server_setup)
309317
assert rate_sec >= occur.count(200) >= (rate_sec - 2)
318+
319+
@pytest.mark.parametrize("src", [rl_vs_pri_sca_src])
320+
def test_rl_policy_scaled(
321+
self,
322+
kube_apis,
323+
ingress_controller_prerequisites,
324+
crd_ingress_controller,
325+
virtual_server_setup,
326+
test_namespace,
327+
src,
328+
):
329+
"""
330+
Test if rate-limit scaling is being calculated correctly
331+
"""
332+
ns = ingress_controller_prerequisites.namespace
333+
scale_deployment(kube_apis.v1, kube_apis.apps_v1_api, "nginx-ingress", ns, 4)
334+
335+
print(f"Create rl policy")
336+
pol_name = create_policy_from_yaml(kube_apis.custom_objects, rl_pol_pri_sca_src, test_namespace)
337+
print(f"Patch vs with policy: {src}")
338+
patch_virtual_server_from_yaml(
339+
kube_apis.custom_objects,
340+
virtual_server_setup.vs_name,
341+
src,
342+
virtual_server_setup.namespace,
343+
)
344+
wait_before_test()
345+
346+
policy_info = read_custom_resource(kube_apis.custom_objects, test_namespace, "policies", pol_name)
347+
ic_pods = get_pod_list(kube_apis.v1, ns)
348+
for i in range(len(ic_pods)):
349+
conf = get_vs_nginx_template_conf(
350+
kube_apis.v1,
351+
virtual_server_setup.namespace,
352+
virtual_server_setup.vs_name,
353+
ic_pods[i].metadata.name,
354+
ingress_controller_prerequisites.namespace,
355+
)
356+
assert "rate=10r/s" in conf
357+
# restore replicas, policy and vs
358+
scale_deployment(kube_apis.v1, kube_apis.apps_v1_api, "nginx-ingress", ns, 1)
359+
delete_policy(kube_apis.custom_objects, pol_name, test_namespace)
360+
self.restore_default_vs(kube_apis, virtual_server_setup)
361+
assert (
362+
policy_info["status"]
363+
and policy_info["status"]["reason"] == "AddedOrUpdated"
364+
and policy_info["status"]["state"] == "Valid"
365+
)

tests/suite/test_rl_policies_vsr.py

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,18 @@
55
from settings import TEST_DATA
66
from suite.utils.custom_resources_utils import read_custom_resource
77
from suite.utils.policy_resources_utils import create_policy_from_yaml, delete_policy
8-
from suite.utils.resources_utils import wait_before_test
9-
from suite.utils.vs_vsr_resources_utils import patch_v_s_route_from_yaml, patch_virtual_server_from_yaml
8+
from suite.utils.resources_utils import get_pod_list, scale_deployment, wait_before_test
9+
from suite.utils.vs_vsr_resources_utils import (
10+
get_vs_nginx_template_conf,
11+
patch_v_s_route_from_yaml,
12+
patch_virtual_server_from_yaml,
13+
)
1014

1115
std_vs_src = f"{TEST_DATA}/virtual-server-route/standard/virtual-server.yaml"
1216
rl_pol_pri_src = f"{TEST_DATA}/rate-limit/policies/rate-limit-primary.yaml"
17+
rl_pol_pri_sca_src = f"{TEST_DATA}/rate-limit/policies/rate-limit-primary-scaled.yaml"
1318
rl_vsr_pri_src = f"{TEST_DATA}/rate-limit/route-subroute/virtual-server-route-pri-subroute.yaml"
19+
rl_vsr_pri_sca_src = f"{TEST_DATA}/rate-limit/route-subroute/virtual-server-route-pri-subroute-scaled.yaml"
1420
rl_pol_sec_src = f"{TEST_DATA}/rate-limit/policies/rate-limit-secondary.yaml"
1521
rl_vsr_sec_src = f"{TEST_DATA}/rate-limit/route-subroute/virtual-server-route-sec-subroute.yaml"
1622
rl_pol_invalid_src = f"{TEST_DATA}/rate-limit/policies/rate-limit-invalid.yaml"
@@ -355,3 +361,58 @@ def test_override_vs_vsr(
355361
kube_apis.custom_objects, v_s_route_setup.vs_name, std_vs_src, v_s_route_setup.namespace
356362
)
357363
assert rate_sec >= occur.count(200) >= (rate_sec - 2)
364+
365+
@pytest.mark.parametrize("src", [rl_vsr_pri_sca_src])
366+
def test_rl_policy_scaled_vsr(
367+
self,
368+
kube_apis,
369+
ingress_controller_prerequisites,
370+
crd_ingress_controller,
371+
v_s_route_app_setup,
372+
v_s_route_setup,
373+
test_namespace,
374+
src,
375+
):
376+
"""
377+
Test if rate-limiting policy is working with ~1 rps in vsr:subroute
378+
"""
379+
380+
ns = ingress_controller_prerequisites.namespace
381+
scale_deployment(kube_apis.v1, kube_apis.apps_v1_api, "nginx-ingress", ns, 4)
382+
383+
print(f"Create rl policy")
384+
pol_name = create_policy_from_yaml(
385+
kube_apis.custom_objects, rl_pol_pri_sca_src, v_s_route_setup.route_m.namespace
386+
)
387+
print(f"Patch vsr with policy: {src}")
388+
patch_v_s_route_from_yaml(
389+
kube_apis.custom_objects,
390+
v_s_route_setup.route_m.name,
391+
src,
392+
v_s_route_setup.route_m.namespace,
393+
)
394+
395+
wait_before_test()
396+
policy_info = read_custom_resource(
397+
kube_apis.custom_objects, v_s_route_setup.route_m.namespace, "policies", pol_name
398+
)
399+
400+
ic_pods = get_pod_list(kube_apis.v1, ns)
401+
for i in range(len(ic_pods)):
402+
conf = get_vs_nginx_template_conf(
403+
kube_apis.v1,
404+
v_s_route_setup.route_m.namespace,
405+
v_s_route_setup.vs_name,
406+
ic_pods[i].metadata.name,
407+
ingress_controller_prerequisites.namespace,
408+
)
409+
assert "rate=10r/s" in conf
410+
# restore replicas, policy and vsr
411+
scale_deployment(kube_apis.v1, kube_apis.apps_v1_api, "nginx-ingress", ns, 1)
412+
delete_policy(kube_apis.custom_objects, pol_name, v_s_route_setup.route_m.namespace)
413+
self.restore_default_vsr(kube_apis, v_s_route_setup)
414+
assert (
415+
policy_info["status"]
416+
and policy_info["status"]["reason"] == "AddedOrUpdated"
417+
and policy_info["status"]["state"] == "Valid"
418+
)

tests/suite/utils/resources_utils.py

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -293,13 +293,24 @@ def wait_until_all_pods_are_ready(v1: CoreV1Api, namespace) -> None:
293293
while not are_all_pods_in_ready_state(v1, namespace) and counter < 200:
294294
# remove counter based condition from line #264 and #269 if --batch-start="True"
295295
print("There are pods that are not Ready. Wait for 1 sec...")
296-
time.sleep(1)
296+
wait_before_test()
297297
counter = counter + 1
298298
if counter >= 300:
299299
raise PodNotReadyException()
300300
print("All pods are Ready")
301301

302302

303+
def get_pod_list(v1: CoreV1Api, namespace) -> []:
304+
"""
305+
Get a list of pods in a namespace.
306+
307+
:param v1: CoreV1Api
308+
:param namespace: namespace
309+
:return: []
310+
"""
311+
return v1.list_namespaced_pod(namespace).items
312+
313+
303314
def get_first_pod_name(v1: CoreV1Api, namespace) -> str:
304315
"""
305316
Return 1st pod_name in a list of pods in a namespace.
@@ -901,16 +912,26 @@ def get_file_contents(v1: CoreV1Api, file_path, pod_name, pod_namespace, print_l
901912
:return: str
902913
"""
903914
command = ["cat", file_path]
904-
resp = stream(
905-
v1.connect_get_namespaced_pod_exec,
906-
pod_name,
907-
pod_namespace,
908-
command=command,
909-
stderr=True,
910-
stdin=False,
911-
stdout=True,
912-
tty=False,
913-
)
915+
retries = 0
916+
while retries <= 3:
917+
wait_before_test()
918+
try:
919+
resp = stream(
920+
v1.connect_get_namespaced_pod_exec,
921+
pod_name,
922+
pod_namespace,
923+
command=command,
924+
stderr=True,
925+
stdin=False,
926+
stdout=True,
927+
tty=False,
928+
)
929+
break
930+
except Exception as e:
931+
print(f"Error: {e}")
932+
retries += 1
933+
if retries == 3:
934+
raise e
914935
result_conf = str(resp)
915936
if print_log:
916937
print("\nFile contents:\n" + result_conf)

0 commit comments

Comments
 (0)