github.com/nginxinc/kubernetes-ingress@v1.12.5/perf-tests/suite/test_ap_reload_perf.py (about) 1 import requests, logging 2 import pytest, json 3 import os, re, yaml, subprocess 4 from datetime import datetime 5 from settings import TEST_DATA, DEPLOYMENTS 6 from suite.custom_resources_utils import ( 7 create_ap_logconf_from_yaml, 8 create_ap_policy_from_yaml, 9 delete_ap_policy, 10 delete_ap_logconf, 11 ) 12 from kubernetes.client import V1ContainerPort 13 from suite.resources_utils import ( 14 wait_before_test, 15 create_example_app, 16 wait_until_all_pods_are_ready, 17 create_items_from_yaml, 18 delete_items_from_yaml, 19 delete_common_app, 20 ensure_connection_to_public_endpoint, 21 create_ingress, 22 create_ingress_with_ap_annotations, 23 replace_ingress_with_ap_annotations, 24 ensure_response_from_backend, 25 wait_before_test, 26 get_events, 27 get_ingress_nginx_template_conf, 28 get_first_pod_name, 29 wait_for_event_increment, 30 get_file_contents, 31 ) 32 from suite.custom_resources_utils import read_ap_crd 33 from suite.yaml_utils import get_first_ingress_host_from_yaml 34 35 ap_policy = "dataguard-alarm" 36 valid_resp_addr = "Server address:" 37 valid_resp_name = "Server name:" 38 invalid_resp_title = "Request Rejected" 39 invalid_resp_body = "The requested URL was rejected. Please consult with your administrator." 40 reload_ap = [] 41 reload_ap_path = [] 42 reload_ap_with_ingress = [] 43 44 45 class AppProtectSetup: 46 """ 47 Encapsulate the example details. 48 Attributes: 49 req_url (str): 50 """ 51 52 def __init__(self, req_url): 53 self.req_url = req_url 54 55 56 @pytest.fixture(scope="class") 57 def enable_prometheus_port( 58 cli_arguments, kube_apis, ingress_controller_prerequisites, crd_ingress_controller_with_ap 59 ) -> None: 60 61 namespace = ingress_controller_prerequisites.namespace 62 port = V1ContainerPort(9113, None, None, "prometheus", "TCP") 63 print("------------------------- Enable 9113 port in IC ----------------------------") 64 body = kube_apis.apps_v1_api.read_namespaced_deployment("nginx-ingress", namespace) 65 body.spec.template.spec.containers[0].ports.append(port) 66 kube_apis.apps_v1_api.patch_namespaced_deployment("nginx-ingress", namespace, body) 67 wait_until_all_pods_are_ready(kube_apis.v1, namespace) 68 69 70 @pytest.fixture(scope="class") 71 def appprotect_setup( 72 request, kube_apis, ingress_controller_endpoint, test_namespace 73 ) -> AppProtectSetup: 74 """ 75 Deploy simple application and all the AppProtect(dataguard-alarm) resources under test in one namespace. 76 77 :param request: pytest fixture 78 :param kube_apis: client apis 79 :param ingress_controller_endpoint: public endpoint 80 :param test_namespace: 81 :return: BackendSetup 82 """ 83 84 print("------------------------- Deploy simple backend application -------------------------") 85 create_example_app(kube_apis, "simple", test_namespace) 86 req_url = f"https://{ingress_controller_endpoint.public_ip}:{ingress_controller_endpoint.port_ssl}/backend1" 87 wait_until_all_pods_are_ready(kube_apis.v1, test_namespace) 88 ensure_connection_to_public_endpoint( 89 ingress_controller_endpoint.public_ip, 90 ingress_controller_endpoint.port, 91 ingress_controller_endpoint.port_ssl, 92 ) 93 94 print("------------------------- Deploy Secret -----------------------------") 95 src_sec_yaml = f"{TEST_DATA}/appprotect/appprotect-secret.yaml" 96 create_items_from_yaml(kube_apis, src_sec_yaml, test_namespace) 97 98 print("------------------------- Deploy logconf -----------------------------") 99 src_log_yaml = f"{TEST_DATA}/appprotect/logconf.yaml" 100 log_name = create_ap_logconf_from_yaml(kube_apis.custom_objects, src_log_yaml, test_namespace) 101 102 print(f"------------------------- Deploy dataguard-alarm appolicy ---------------------------") 103 src_pol_yaml = f"{TEST_DATA}/appprotect/{ap_policy}.yaml" 104 pol_name = create_ap_policy_from_yaml(kube_apis.custom_objects, src_pol_yaml, test_namespace) 105 106 def fin(): 107 print("Clean up:") 108 delete_ap_policy(kube_apis.custom_objects, pol_name, test_namespace) 109 delete_ap_logconf(kube_apis.custom_objects, log_name, test_namespace) 110 delete_common_app(kube_apis, "simple", test_namespace) 111 src_sec_yaml = f"{TEST_DATA}/appprotect/appprotect-secret.yaml" 112 delete_items_from_yaml(kube_apis, src_sec_yaml, test_namespace) 113 with open("reload_ap.json", "w+") as f: 114 json.dump(reload_ap, f, ensure_ascii=False, indent=4) 115 with open("reload_ap_path.json", "w+") as f: 116 json.dump(reload_ap_path, f, ensure_ascii=False, indent=4) 117 with open("reload_ap_with_ingress.json", "w+") as f: 118 json.dump(reload_ap_with_ingress, f, ensure_ascii=False, indent=4) 119 120 request.addfinalizer(fin) 121 122 return AppProtectSetup(req_url) 123 124 125 @pytest.fixture 126 def setup_users(request): 127 return request.config.getoption("--users") 128 129 130 @pytest.fixture 131 def setup_rate(request): 132 return request.config.getoption("--hatch-rate") 133 134 135 @pytest.fixture 136 def setup_time(request): 137 return request.config.getoption("--time") 138 139 140 def assert_invalid_responses(response) -> None: 141 """ 142 Assert responses when policy config is blocking requests 143 :param response: Response 144 """ 145 assert invalid_resp_title in response.text 146 assert invalid_resp_body in response.text 147 assert response.status_code == 200 148 149 150 @pytest.mark.ap_perf 151 @pytest.mark.parametrize( 152 "crd_ingress_controller_with_ap", 153 [ 154 { 155 "extra_args": [ 156 f"-enable-custom-resources", 157 f"-enable-app-protect", 158 f"-enable-prometheus-metrics", 159 ] 160 } 161 ], 162 indirect=["crd_ingress_controller_with_ap"], 163 ) 164 class TestAppProtectPerf: 165 def collect_prom_reload_metrics(self, metric_list, scenario, ip, port) -> None: 166 req_url = f"http://{ip}:{port}/metrics" 167 resp = requests.get(req_url) 168 resp_decoded = resp.content.decode("utf-8") 169 reload_metric = "" 170 for line in resp_decoded.splitlines(): 171 if "last_reload_milliseconds{class" in line: 172 reload_metric = re.findall("\d+", line)[0] 173 metric_list.append( 174 { 175 f"Reload time ({scenario}) ": f"{reload_metric}ms", 176 "TimeStamp": str(datetime.utcnow()), 177 } 178 ) 179 180 def test_ap_perf_create_ingress( 181 self, 182 kube_apis, 183 ingress_controller_prerequisites, 184 ingress_controller_endpoint, 185 crd_ingress_controller_with_ap, 186 appprotect_setup, 187 enable_prometheus_port, 188 test_namespace, 189 ): 190 """ 191 Test reload times for creating AP ingress 192 """ 193 194 src_ing_yaml = f"{TEST_DATA}/appprotect/appprotect-ingress.yaml" 195 create_ingress_with_ap_annotations( 196 kube_apis, src_ing_yaml, test_namespace, ap_policy, "True", "True", "127.0.0.1:514" 197 ) 198 ingress_host = get_first_ingress_host_from_yaml(src_ing_yaml) 199 200 print("--------- Run test while AppProtect module is enabled with correct policy ---------") 201 ensure_response_from_backend(appprotect_setup.req_url, ingress_host) 202 wait_before_test(40) 203 response = requests.get( 204 appprotect_setup.req_url + "/<script>", headers={"host": ingress_host}, verify=False 205 ) 206 print(response.text) 207 self.collect_prom_reload_metrics( 208 reload_ap, 209 "creating AP ingress", 210 ingress_controller_endpoint.public_ip, 211 ingress_controller_endpoint.metrics_port, 212 ) 213 delete_items_from_yaml(kube_apis, src_ing_yaml, test_namespace) 214 assert_invalid_responses(response) 215 216 def test_ap_perf_ingress_path_change( 217 self, 218 kube_apis, 219 ingress_controller_prerequisites, 220 ingress_controller_endpoint, 221 crd_ingress_controller_with_ap, 222 appprotect_setup, 223 enable_prometheus_port, 224 test_namespace, 225 ): 226 """ 227 Test reload times for changing paths 228 """ 229 230 src1_ing_yaml = f"{TEST_DATA}/appprotect/appprotect-ingress.yaml" 231 print(src1_ing_yaml) 232 src2_ing_yaml = os.path.join(os.path.dirname(__file__), "../data/appprotect-ingress.yaml") 233 print(src2_ing_yaml) 234 create_ingress_with_ap_annotations( 235 kube_apis, src1_ing_yaml, test_namespace, ap_policy, "True", "True", "127.0.0.1:514" 236 ) 237 ingress_host = get_first_ingress_host_from_yaml(src1_ing_yaml) 238 239 print("--------- Run test while AppProtect module is enabled with correct policy ---------") 240 ensure_response_from_backend(appprotect_setup.req_url, ingress_host) 241 wait_before_test(30) 242 replace_ingress_with_ap_annotations( 243 kube_apis, 244 src2_ing_yaml, 245 "appprotect-ingress", 246 test_namespace, 247 ap_policy, 248 "True", 249 "True", 250 "127.0.0.1:514", 251 ) 252 ensure_response_from_backend(appprotect_setup.req_url, ingress_host) 253 wait_before_test(30) 254 response = "" 255 response = requests.get( 256 appprotect_setup.req_url + "/v1/<script>", headers={"host": ingress_host}, verify=False 257 ) 258 print(response.text) 259 self.collect_prom_reload_metrics( 260 reload_ap_path, 261 "changing paths in AP ingress", 262 ingress_controller_endpoint.public_ip, 263 ingress_controller_endpoint.metrics_port, 264 ) 265 delete_items_from_yaml(kube_apis, src2_ing_yaml, test_namespace) 266 assert_invalid_responses(response) 267 268 def test_ap_perf_multiple_ingress( 269 self, 270 kube_apis, 271 ingress_controller_prerequisites, 272 ingress_controller_endpoint, 273 crd_ingress_controller_with_ap, 274 appprotect_setup, 275 enable_prometheus_port, 276 test_namespace, 277 ): 278 """ 279 Test reload times for creating AP ingress while a simple ingress exists. 280 """ 281 282 src1_ing_yaml = f"{TEST_DATA}/appprotect/appprotect-ingress.yaml" 283 print(src1_ing_yaml) 284 src2_ing_yaml = os.path.join(os.path.dirname(__file__), "../data/non-ap-ingress.yaml") 285 print(src2_ing_yaml) 286 287 with open(src2_ing_yaml) as f: 288 doc = yaml.safe_load(f) 289 # create ingress without AP annotation 290 create_ingress(kube_apis.extensions_v1_beta1, test_namespace, doc) 291 wait_before_test(10) 292 # create ingress with AP annotations 293 create_ingress_with_ap_annotations( 294 kube_apis, src1_ing_yaml, test_namespace, ap_policy, "True", "True", "127.0.0.1:514" 295 ) 296 ingress_host = get_first_ingress_host_from_yaml(src1_ing_yaml) 297 298 print("--------- Run test while AppProtect module is enabled with correct policy ---------") 299 ensure_response_from_backend(appprotect_setup.req_url, ingress_host) 300 wait_before_test(30) 301 response = requests.get( 302 appprotect_setup.req_url + "/<script>", headers={"host": ingress_host}, verify=False 303 ) 304 print(response.text) 305 self.collect_prom_reload_metrics( 306 reload_ap_with_ingress, 307 "creating AP ingress alongside a simple ingress", 308 ingress_controller_endpoint.public_ip, 309 ingress_controller_endpoint.metrics_port, 310 ) 311 delete_items_from_yaml(kube_apis, src1_ing_yaml, test_namespace) 312 delete_items_from_yaml(kube_apis, src2_ing_yaml, test_namespace) 313 assert_invalid_responses(response) 314 315 @pytest.mark.ap_resp 316 def test_ap_perf_response( 317 self, 318 kube_apis, 319 ingress_controller_prerequisites, 320 ingress_controller_endpoint, 321 crd_ingress_controller_with_ap, 322 appprotect_setup, 323 enable_prometheus_port, 324 test_namespace, 325 setup_users, 326 setup_time, 327 setup_rate, 328 ): 329 """ 330 Test response times for AP ingress by running locust as a subprocess. 331 """ 332 333 src_ing_yaml = f"{TEST_DATA}/appprotect/appprotect-ingress.yaml" 334 print(src_ing_yaml) 335 336 # create ingress with AP annotations 337 create_ingress_with_ap_annotations( 338 kube_apis, src_ing_yaml, test_namespace, ap_policy, "True", "True", "127.0.0.1:514" 339 ) 340 ingress_host = get_first_ingress_host_from_yaml(src_ing_yaml) 341 342 print("--------- Run test while AppProtect module is enabled with correct policy ---------") 343 ensure_response_from_backend(appprotect_setup.req_url, ingress_host) 344 wait_before_test(30) 345 response = "" 346 response = requests.get( 347 appprotect_setup.req_url + "/<script>", headers={"host": ingress_host}, verify=False 348 ) 349 print(appprotect_setup.req_url + "/<script>") 350 print(ingress_host) 351 print(response.text) 352 # run response time tests using locust.io 353 subprocess.run( 354 [ 355 "locust", 356 "-f", 357 "suite/ap_request_perf.py", 358 "--headless", 359 "--host", 360 appprotect_setup.req_url, 361 "--csv", 362 "ap_response_times", 363 "-u", 364 setup_users, # total no. of users 365 "-r", 366 setup_rate, # no. of users hatched per second 367 "-t", 368 setup_time, # locust session duration in seconds 369 ] 370 ) 371 delete_items_from_yaml(kube_apis, src_ing_yaml, test_namespace) 372 assert_invalid_responses(response)