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)