github.com/nginxinc/kubernetes-ingress@v1.12.5/tests/suite/test_v_s_route_upstream_options.py (about)

     1  import pytest
     2  import requests
     3  from kubernetes.client.rest import ApiException
     4  
     5  from settings import TEST_DATA
     6  from suite.custom_assertions import assert_event_and_get_count, assert_event_count_increased, assert_response_codes, \
     7      assert_event, assert_event_starts_with_text_and_contains_errors, wait_for_event_count_increases
     8  from suite.custom_resources_utils import get_vs_nginx_template_conf, patch_v_s_route_from_yaml, patch_v_s_route, \
     9      generate_item_with_upstream_options
    10  from suite.resources_utils import get_first_pod_name, wait_before_test, replace_configmap_from_yaml, get_events
    11  
    12  
    13  @pytest.mark.parametrize('crd_ingress_controller, v_s_route_setup',
    14                           [({"type": "complete", "extra_args": [f"-enable-custom-resources"]},
    15                             {"example": "virtual-server-route-upstream-options"})],
    16                           indirect=True)
    17  @pytest.mark.vsr
    18  class TestVSRouteUpstreamOptions:
    19      def test_nginx_config_upstreams_defaults(self, kube_apis, ingress_controller_prerequisites,
    20                                               crd_ingress_controller, v_s_route_setup, v_s_route_app_setup):
    21          print("Case 1: no ConfigMap keys, no options in VS")
    22          ic_pod_name = get_first_pod_name(kube_apis.v1, ingress_controller_prerequisites.namespace)
    23          config = get_vs_nginx_template_conf(kube_apis.v1,
    24                                              v_s_route_setup.namespace,
    25                                              v_s_route_setup.vs_name,
    26                                              ic_pod_name,
    27                                              ingress_controller_prerequisites.namespace)
    28  
    29          assert "random two least_conn;" in config
    30          assert "ip_hash;" not in config
    31          assert "hash " not in config
    32          assert "least_time " not in config
    33  
    34          assert "proxy_connect_timeout 60s;" in config
    35          assert "proxy_read_timeout 60s;" in config
    36          assert "proxy_send_timeout 60s;" in config
    37  
    38          assert "max_fails=1 fail_timeout=10s max_conns=0;" in config
    39          assert "slow_start" not in config
    40  
    41          assert "keepalive" not in config
    42          assert 'set $default_connection_header "";' not in config
    43          assert 'set $default_connection_header close;' in config
    44          assert "proxy_set_header Upgrade $http_upgrade;" in config
    45          assert "proxy_set_header Connection $vs_connection_header;" in config
    46          assert "proxy_http_version 1.1;" in config
    47  
    48          assert "proxy_next_upstream error timeout;" in config
    49          assert "proxy_next_upstream_timeout 0s;" in config
    50          assert "proxy_next_upstream_tries 0;" in config
    51  
    52          assert "client_max_body_size 1m;" in config
    53  
    54          assert "proxy_buffer_size" not in config
    55          assert "proxy_buffering on;" in config
    56          assert "proxy_buffers" not in config
    57  
    58          assert "sticky cookie" not in config
    59  
    60      @pytest.mark.parametrize('options, expected_strings', [
    61          ({"lb-method": "least_conn", "max-fails": 8,
    62            "fail-timeout": "13s", "connect-timeout": "55s", "read-timeout": "1s", "send-timeout": "1h",
    63            "keepalive": 54, "max-conns": 1024, "client-max-body-size": "1048K",
    64            "buffering": True, "buffer-size": "2k", "buffers": {"number": 4, "size": "2k"}},
    65           ["least_conn;", "max_fails=8 ",
    66            "fail_timeout=13s ", "proxy_connect_timeout 55s;", "proxy_read_timeout 1s;",
    67            "proxy_send_timeout 1h;", "keepalive 54;", 'set $default_connection_header "";', "max_conns=1024;",
    68            "client_max_body_size 1048K;",
    69            "proxy_buffering on;", "proxy_buffer_size 2k;", "proxy_buffers 4 2k;"]),
    70          ({"lb-method": "ip_hash", "connect-timeout": "75", "read-timeout": "15", "send-timeout": "1h"},
    71           ["ip_hash;", "proxy_connect_timeout 75s;", "proxy_read_timeout 15s;", "proxy_send_timeout 1h;"]),
    72          ({"connect-timeout": "1m", "read-timeout": "1m", "send-timeout": "1s"},
    73           ["proxy_connect_timeout 1m;", "proxy_read_timeout 1m;", "proxy_send_timeout 1s;"]),
    74          ({"next-upstream": "error timeout non_idempotent", "next-upstream-timeout": "5s", "next-upstream-tries": 10},
    75           ["proxy_next_upstream error timeout non_idempotent;",
    76            "proxy_next_upstream_timeout 5s;", "proxy_next_upstream_tries 10;"])
    77      ])
    78      def test_when_option_in_v_s_r_only(self, kube_apis,
    79                                         ingress_controller_prerequisites,
    80                                         crd_ingress_controller,
    81                                         v_s_route_setup, v_s_route_app_setup,
    82                                         options, expected_strings):
    83          req_url = f"http://{v_s_route_setup.public_endpoint.public_ip}:{v_s_route_setup.public_endpoint.port}"
    84          text_s = f"{v_s_route_setup.route_s.namespace}/{v_s_route_setup.route_s.name}"
    85          text_m = f"{v_s_route_setup.route_m.namespace}/{v_s_route_setup.route_m.name}"
    86          vsr_s_event_text = f"Configuration for {text_s} was added or updated"
    87          vsr_m_event_text = f"Configuration for {text_m} was added or updated"
    88          events_ns_m = get_events(kube_apis.v1, v_s_route_setup.route_m.namespace)
    89          events_ns_s = get_events(kube_apis.v1, v_s_route_setup.route_s.namespace)
    90          initial_count_vsr_m = assert_event_and_get_count(vsr_m_event_text, events_ns_m)
    91          initial_count_vsr_s = assert_event_and_get_count(vsr_s_event_text, events_ns_s)
    92          print(f"Case 2: no key in ConfigMap, option specified in VSR")
    93          new_body_m = generate_item_with_upstream_options(
    94              f"{TEST_DATA}/virtual-server-route-upstream-options/route-multiple.yaml",
    95              options)
    96          new_body_s = generate_item_with_upstream_options(
    97              f"{TEST_DATA}/virtual-server-route-upstream-options/route-single.yaml",
    98              options)
    99          patch_v_s_route(kube_apis.custom_objects,
   100                          v_s_route_setup.route_m.name, v_s_route_setup.route_m.namespace, new_body_m)
   101          patch_v_s_route(kube_apis.custom_objects,
   102                          v_s_route_setup.route_s.name, v_s_route_setup.route_s.namespace, new_body_s)
   103          wait_before_test(1)
   104          ic_pod_name = get_first_pod_name(kube_apis.v1, ingress_controller_prerequisites.namespace)
   105          config = get_vs_nginx_template_conf(kube_apis.v1,
   106                                              v_s_route_setup.namespace,
   107                                              v_s_route_setup.vs_name,
   108                                              ic_pod_name,
   109                                              ingress_controller_prerequisites.namespace)
   110          resp_1 = requests.get(f"{req_url}{v_s_route_setup.route_m.paths[0]}",
   111                                headers={"host": v_s_route_setup.vs_host})
   112          resp_2 = requests.get(f"{req_url}{v_s_route_setup.route_s.paths[0]}",
   113                                headers={"host": v_s_route_setup.vs_host})
   114  
   115          wait_for_event_count_increases(kube_apis, vsr_s_event_text,
   116                                         initial_count_vsr_s, v_s_route_setup.route_s.namespace)
   117          wait_for_event_count_increases(kube_apis, vsr_m_event_text,
   118                                         initial_count_vsr_m, v_s_route_setup.route_m.namespace)
   119  
   120          for _ in expected_strings:
   121              assert _ in config
   122          assert_response_codes(resp_1, resp_2)
   123  
   124      @pytest.mark.parametrize('config_map_file, expected_strings, unexpected_strings', [
   125          (f"{TEST_DATA}/virtual-server-route-upstream-options/configmap-with-keys.yaml",
   126           ["max_fails=3 ", "fail_timeout=33s ", "max_conns=0;",
   127            "proxy_connect_timeout 44s;", "proxy_read_timeout 22s;", "proxy_send_timeout 55s;",
   128            "keepalive 1024;", 'set $default_connection_header "";',
   129            "client_max_body_size 3m;",
   130            "proxy_buffering off;", "proxy_buffer_size 1k;", "proxy_buffers 8 1k;"],
   131           ["ip_hash;", "least_conn;", "random ", "hash", "least_time ",
   132            "max_fails=1 ", "fail_timeout=10s ", "max_conns=1000;",
   133            "proxy_connect_timeout 60s;", "proxy_read_timeout 60s;", "proxy_send_timeout 60s;",
   134            "client_max_body_size 1m;",
   135            "slow_start=0s",
   136            "proxy_buffering on;"]),
   137      ])
   138      def test_when_option_in_config_map_only(self, kube_apis,
   139                                              ingress_controller_prerequisites,
   140                                              crd_ingress_controller,
   141                                              v_s_route_setup, v_s_route_app_setup, restore_configmap,
   142                                              config_map_file, expected_strings, unexpected_strings):
   143          req_url = f"http://{v_s_route_setup.public_endpoint.public_ip}:{v_s_route_setup.public_endpoint.port}"
   144          text_s = f"{v_s_route_setup.route_s.namespace}/{v_s_route_setup.route_s.name}"
   145          text_m = f"{v_s_route_setup.route_m.namespace}/{v_s_route_setup.route_m.name}"
   146          vsr_s_event_text = f"Configuration for {text_s} was added or updated"
   147          vsr_m_event_text = f"Configuration for {text_m} was added or updated"
   148          print(f"Case 3: key specified in ConfigMap, no option in VS")
   149          patch_v_s_route_from_yaml(kube_apis.custom_objects, v_s_route_setup.route_m.name,
   150                                    f"{TEST_DATA}/virtual-server-route-upstream-options/route-multiple.yaml",
   151                                    v_s_route_setup.route_m.namespace)
   152          patch_v_s_route_from_yaml(kube_apis.custom_objects, v_s_route_setup.route_s.name,
   153                                    f"{TEST_DATA}/virtual-server-route-upstream-options/route-single.yaml",
   154                                    v_s_route_setup.route_s.namespace)
   155          config_map_name = ingress_controller_prerequisites.config_map["metadata"]["name"]
   156          replace_configmap_from_yaml(kube_apis.v1, config_map_name,
   157                                      ingress_controller_prerequisites.namespace,
   158                                      config_map_file)
   159          wait_before_test()
   160          ic_pod_name = get_first_pod_name(kube_apis.v1, ingress_controller_prerequisites.namespace)
   161          config = get_vs_nginx_template_conf(kube_apis.v1,
   162                                              v_s_route_setup.namespace,
   163                                              v_s_route_setup.vs_name,
   164                                              ic_pod_name,
   165                                              ingress_controller_prerequisites.namespace)
   166          resp_1 = requests.get(f"{req_url}{v_s_route_setup.route_m.paths[0]}",
   167                                headers={"host": v_s_route_setup.vs_host})
   168          resp_2 = requests.get(f"{req_url}{v_s_route_setup.route_s.paths[0]}",
   169                                headers={"host": v_s_route_setup.vs_host})
   170          vsr_s_events = get_events(kube_apis.v1, v_s_route_setup.route_s.namespace)
   171          vsr_m_events = get_events(kube_apis.v1, v_s_route_setup.route_m.namespace)
   172  
   173          assert_event(vsr_m_event_text, vsr_m_events)
   174          assert_event(vsr_s_event_text, vsr_s_events)
   175          for _ in expected_strings:
   176              assert _ in config
   177          for _ in unexpected_strings:
   178              assert _ not in config
   179          assert_response_codes(resp_1, resp_2)
   180  
   181      @pytest.mark.parametrize('options, expected_strings, unexpected_strings', [
   182          ({"lb-method": "least_conn", "max-fails": 12,
   183            "fail-timeout": "1m", "connect-timeout": "1m", "read-timeout": "77s", "send-timeout": "23s",
   184            "keepalive": 48, "client-max-body-size": "0",
   185            "buffering": True, "buffer-size": "2k", "buffers": {"number": 4, "size": "2k"}},
   186           ["least_conn;", "max_fails=12 ",
   187            "fail_timeout=1m ", "max_conns=0;",
   188            "proxy_connect_timeout 1m;", "proxy_read_timeout 77s;", "proxy_send_timeout 23s;",
   189            "keepalive 48;", 'set $default_connection_header "";',
   190            "client_max_body_size 0;",
   191            "proxy_buffering on;", "proxy_buffer_size 2k;", "proxy_buffers 4 2k;"],
   192           ["ip_hash;", "random ", "hash", "least_time ", "max_fails=1 ", "fail_timeout=10s ",
   193            "proxy_connect_timeout 44s;", "proxy_read_timeout 22s;", "proxy_send_timeout 55s;",
   194            "keepalive 1024;",
   195            "client_max_body_size 3m;", "client_max_body_size 1m;",
   196            "proxy_buffering off;", "proxy_buffer_size 1k;", "proxy_buffers 8 1k;"])
   197      ])
   198      def test_v_s_r_overrides_config_map(self, kube_apis,
   199                                          ingress_controller_prerequisites,
   200                                          crd_ingress_controller, v_s_route_setup, v_s_route_app_setup, restore_configmap,
   201                                          options, expected_strings, unexpected_strings):
   202          req_url = f"http://{v_s_route_setup.public_endpoint.public_ip}:{v_s_route_setup.public_endpoint.port}"
   203          text_s = f"{v_s_route_setup.route_s.namespace}/{v_s_route_setup.route_s.name}"
   204          text_m = f"{v_s_route_setup.route_m.namespace}/{v_s_route_setup.route_m.name}"
   205          vsr_s_event_text = f"Configuration for {text_s} was added or updated"
   206          vsr_m_event_text = f"Configuration for {text_m} was added or updated"
   207          events_ns_m = get_events(kube_apis.v1, v_s_route_setup.route_m.namespace)
   208          events_ns_s = get_events(kube_apis.v1, v_s_route_setup.route_s.namespace)
   209          initial_count_vsr_m = assert_event_and_get_count(vsr_m_event_text, events_ns_m)
   210          initial_count_vsr_s = assert_event_and_get_count(vsr_s_event_text, events_ns_s)
   211          print(f"Case 4: key specified in ConfigMap, option specified in VS")
   212          new_body_m = generate_item_with_upstream_options(
   213              f"{TEST_DATA}/virtual-server-route-upstream-options/route-multiple.yaml",
   214              options)
   215          new_body_s = generate_item_with_upstream_options(
   216              f"{TEST_DATA}/virtual-server-route-upstream-options/route-single.yaml",
   217              options)
   218          patch_v_s_route(kube_apis.custom_objects,
   219                          v_s_route_setup.route_m.name, v_s_route_setup.route_m.namespace, new_body_m)
   220          patch_v_s_route(kube_apis.custom_objects,
   221                          v_s_route_setup.route_s.name, v_s_route_setup.route_s.namespace, new_body_s)
   222          config_map_name = ingress_controller_prerequisites.config_map["metadata"]["name"]
   223          replace_configmap_from_yaml(kube_apis.v1, config_map_name,
   224                                      ingress_controller_prerequisites.namespace,
   225                                      f"{TEST_DATA}/virtual-server-route-upstream-options/configmap-with-keys.yaml")
   226          wait_before_test()
   227          ic_pod_name = get_first_pod_name(kube_apis.v1, ingress_controller_prerequisites.namespace)
   228          config = get_vs_nginx_template_conf(kube_apis.v1,
   229                                              v_s_route_setup.namespace,
   230                                              v_s_route_setup.vs_name,
   231                                              ic_pod_name,
   232                                              ingress_controller_prerequisites.namespace)
   233          resp_1 = requests.get(f"{req_url}{v_s_route_setup.route_m.paths[0]}",
   234                                headers={"host": v_s_route_setup.vs_host})
   235          resp_2 = requests.get(f"{req_url}{v_s_route_setup.route_s.paths[0]}",
   236                                headers={"host": v_s_route_setup.vs_host})
   237          vsr_s_events = get_events(kube_apis.v1, v_s_route_setup.route_s.namespace)
   238          vsr_m_events = get_events(kube_apis.v1, v_s_route_setup.route_m.namespace)
   239  
   240          assert_event_count_increased(vsr_m_event_text, initial_count_vsr_m, vsr_m_events)
   241          assert_event_count_increased(vsr_s_event_text, initial_count_vsr_s, vsr_s_events)
   242          for _ in expected_strings:
   243              assert _ in config
   244          for _ in unexpected_strings:
   245              assert _ not in config
   246          assert_response_codes(resp_1, resp_2)
   247  
   248  
   249  @pytest.mark.vsr
   250  @pytest.mark.parametrize('crd_ingress_controller, v_s_route_setup',
   251                           [({"type": "complete", "extra_args": [f"-enable-custom-resources"]},
   252                             {"example": "virtual-server-route-upstream-options"})],
   253                           indirect=True)
   254  class TestVSRouteUpstreamOptionsValidation:
   255      def test_event_message_and_config(self, kube_apis, ingress_controller_prerequisites,
   256                                        crd_ingress_controller, v_s_route_setup):
   257          invalid_fields_s = [
   258              "upstreams[0].lb-method", "upstreams[0].fail-timeout",
   259              "upstreams[0].max-fails", "upstreams[0].connect-timeout",
   260              "upstreams[0].read-timeout", "upstreams[0].send-timeout",
   261              "upstreams[0].keepalive", "upstreams[0].max-conns",
   262              "upstreams[0].next-upstream",
   263              "upstreams[0].next-upstream-timeout", "upstreams[0].next-upstream-tries",
   264              "upstreams[0].client-max-body-size",
   265              "upstreams[0].buffers.number", "upstreams[0].buffers.size", "upstreams[0].buffer-size"
   266              ]
   267          invalid_fields_m = [
   268              "upstreams[0].lb-method", "upstreams[0].fail-timeout",
   269              "upstreams[0].max-fails", "upstreams[0].connect-timeout",
   270              "upstreams[0].read-timeout", "upstreams[0].send-timeout",
   271              "upstreams[0].keepalive", "upstreams[0].max-conns",
   272              "upstreams[0].next-upstream",
   273              "upstreams[0].next-upstream-timeout", "upstreams[0].next-upstream-tries",
   274              "upstreams[0].client-max-body-size",
   275              "upstreams[0].buffers.number", "upstreams[0].buffers.size", "upstreams[0].buffer-size",
   276              "upstreams[1].lb-method", "upstreams[1].fail-timeout",
   277              "upstreams[1].max-fails", "upstreams[1].connect-timeout",
   278              "upstreams[1].read-timeout", "upstreams[1].send-timeout",
   279              "upstreams[1].keepalive", "upstreams[1].max-conns",
   280              "upstreams[1].next-upstream",
   281              "upstreams[1].next-upstream-timeout", "upstreams[1].next-upstream-tries",
   282              "upstreams[1].client-max-body-size",
   283              "upstreams[1].buffers.number", "upstreams[1].buffers.size", "upstreams[1].buffer-size"
   284              ]
   285          text_s = f"{v_s_route_setup.route_s.namespace}/{v_s_route_setup.route_s.name}"
   286          text_m = f"{v_s_route_setup.route_m.namespace}/{v_s_route_setup.route_m.name}"
   287          vsr_s_event_text = f"VirtualServerRoute {text_s} was rejected with error:"
   288          vsr_m_event_text = f"VirtualServerRoute {text_m} was rejected with error:"
   289          patch_v_s_route_from_yaml(kube_apis.custom_objects,
   290                                    v_s_route_setup.route_s.name,
   291                                    f"{TEST_DATA}/virtual-server-route-upstream-options/route-single-invalid-keys.yaml",
   292                                    v_s_route_setup.route_s.namespace)
   293          patch_v_s_route_from_yaml(kube_apis.custom_objects,
   294                                    v_s_route_setup.route_m.name,
   295                                    f"{TEST_DATA}/virtual-server-route-upstream-options/route-multiple-invalid-keys.yaml",
   296                                    v_s_route_setup.route_m.namespace)
   297          wait_before_test(2)
   298          ic_pod_name = get_first_pod_name(kube_apis.v1, ingress_controller_prerequisites.namespace)
   299          config = get_vs_nginx_template_conf(kube_apis.v1,
   300                                              v_s_route_setup.namespace,
   301                                              v_s_route_setup.vs_name,
   302                                              ic_pod_name,
   303                                              ingress_controller_prerequisites.namespace)
   304          vsr_s_events = get_events(kube_apis.v1, v_s_route_setup.route_s.namespace)
   305          vsr_m_events = get_events(kube_apis.v1, v_s_route_setup.route_m.namespace)
   306  
   307          assert_event_starts_with_text_and_contains_errors(vsr_s_event_text, vsr_s_events, invalid_fields_s)
   308          assert_event_starts_with_text_and_contains_errors(vsr_m_event_text, vsr_m_events, invalid_fields_m)
   309          assert "upstream" not in config
   310  
   311      def test_openapi_validation_flow(self, kube_apis, ingress_controller_prerequisites,
   312                                       crd_ingress_controller, v_s_route_setup):
   313          ic_pod_name = get_first_pod_name(kube_apis.v1, ingress_controller_prerequisites.namespace)
   314          invalid_fields = [
   315              "upstreams.lb-method", "upstreams.fail-timeout",
   316              "upstreams.max-fails", "upstreams.connect-timeout",
   317              "upstreams.read-timeout", "upstreams.send-timeout",
   318              "upstreams.keepalive", "upstreams.max-conns",
   319              "upstreams.next-upstream",
   320              "upstreams.next-upstream-timeout", "upstreams.next-upstream-tries",
   321              "upstreams.client-max-body-size",
   322              "upstreams.buffers.number", "upstreams.buffers.size", "upstreams.buffer-size",
   323              "upstreams.buffering", "upstreams.tls"
   324          ]
   325          config_old = get_vs_nginx_template_conf(kube_apis.v1,
   326                                                  v_s_route_setup.namespace,
   327                                                  v_s_route_setup.vs_name,
   328                                                  ic_pod_name,
   329                                                  ingress_controller_prerequisites.namespace)
   330          source_yaml = f"{TEST_DATA}/virtual-server-route-upstream-options/route-single-invalid-keys-openapi.yaml"
   331          try:
   332              patch_v_s_route_from_yaml(kube_apis.custom_objects,
   333                                        v_s_route_setup.route_s.name,
   334                                        source_yaml,
   335                                        v_s_route_setup.route_s.namespace)
   336          except ApiException as ex:
   337              assert ex.status == 422
   338              for item in invalid_fields:
   339                  assert item in ex.body
   340          except Exception as ex:
   341              pytest.fail(f"An unexpected exception is raised: {ex}")
   342          else:
   343              pytest.fail("Expected an exception but there was none")
   344  
   345          wait_before_test(2)
   346          config_new = get_vs_nginx_template_conf(kube_apis.v1,
   347                                                  v_s_route_setup.namespace,
   348                                                  v_s_route_setup.vs_name,
   349                                                  ic_pod_name,
   350                                                  ingress_controller_prerequisites.namespace)
   351          assert config_old == config_new, "Expected: config doesn't change"
   352  
   353  
   354  @pytest.mark.vsr
   355  @pytest.mark.skip_for_nginx_oss
   356  @pytest.mark.parametrize('crd_ingress_controller, v_s_route_setup',
   357                           [({"type": "complete", "extra_args": [f"-enable-custom-resources"]},
   358                             {"example": "virtual-server-route-upstream-options"})],
   359                           indirect=True)
   360  class TestOptionsSpecificForPlus:
   361      @pytest.mark.parametrize('options, expected_strings', [
   362          ({"lb-method": "least_conn",
   363            "healthCheck": {"enable": True, "port": 8080},
   364            "slow-start": "3h",
   365            "queue": {"size": 100},
   366            "sessionCookie": {"enable": True,
   367                              "name": "TestCookie",
   368                              "path": "/some-valid/path",
   369                              "expires": "max",
   370                              "domain": "virtual-server-route.example.com", "httpOnly": True, "secure": True}},
   371           ["health_check uri=/ port=8080 interval=5s jitter=0s", "fails=1 passes=1;",
   372            "slow_start=3h", "queue 100 timeout=60s;",
   373            "sticky cookie TestCookie expires=max domain=virtual-server-route.example.com httponly secure path=/some-valid/path;"]),
   374          ({"lb-method": "least_conn",
   375            "healthCheck": {"enable": True, "path": "/health",
   376                            "interval": "15s", "jitter": "3",
   377                            "fails": 2, "passes": 2, "port": 8080,
   378                            "tls": {"enable": True}, "statusMatch": "200",
   379                            "connect-timeout": "35s", "read-timeout": "45s", "send-timeout": "55s",
   380                            "headers": [{"name": "Host", "value": "virtual-server.example.com"}]},
   381            "slow-start": "0s",
   382            "queue": {"size": 1000, "timeout": "66s"}},
   383           ["health_check uri=/health port=8080 interval=15s jitter=3", "fails=2 passes=2 match=",
   384            "proxy_pass https://vs", "status 200;",
   385            "proxy_connect_timeout 35s;", "proxy_read_timeout 45s;", "proxy_send_timeout 55s;",
   386            'proxy_set_header Host "virtual-server.example.com";',
   387            "slow_start=0s", "queue 1000 timeout=66s;"])
   388      ])
   389      def test_config_and_events(self, kube_apis,
   390                                 ingress_controller_prerequisites,
   391                                 crd_ingress_controller,
   392                                 v_s_route_setup, v_s_route_app_setup,
   393                                 options, expected_strings):
   394          expected_strings.append(f"location @hc-vs_{v_s_route_setup.namespace}_{v_s_route_setup.vs_name}"
   395                                  f"_vsr_{v_s_route_setup.route_m.namespace}_{v_s_route_setup.route_m.name}")
   396          expected_strings.append(f"location @hc-vs_{v_s_route_setup.namespace}_{v_s_route_setup.vs_name}"
   397                                  f"_vsr_{v_s_route_setup.route_s.namespace}_{v_s_route_setup.route_s.name}")
   398          req_url = f"http://{v_s_route_setup.public_endpoint.public_ip}:{v_s_route_setup.public_endpoint.port}"
   399          text_s = f"{v_s_route_setup.route_s.namespace}/{v_s_route_setup.route_s.name}"
   400          text_m = f"{v_s_route_setup.route_m.namespace}/{v_s_route_setup.route_m.name}"
   401          vsr_s_event_text = f"Configuration for {text_s} was added or updated"
   402          vsr_m_event_text = f"Configuration for {text_m} was added or updated"
   403          events_ns_m = get_events(kube_apis.v1, v_s_route_setup.route_m.namespace)
   404          events_ns_s = get_events(kube_apis.v1, v_s_route_setup.route_s.namespace)
   405          initial_count_vsr_m = assert_event_and_get_count(vsr_m_event_text, events_ns_m)
   406          initial_count_vsr_s = assert_event_and_get_count(vsr_s_event_text, events_ns_s)
   407          print(f"Case 2: no key in ConfigMap, option specified in VSR")
   408          new_body_m = generate_item_with_upstream_options(
   409              f"{TEST_DATA}/virtual-server-route-upstream-options/route-multiple.yaml",
   410              options)
   411          new_body_s = generate_item_with_upstream_options(
   412              f"{TEST_DATA}/virtual-server-route-upstream-options/route-single.yaml",
   413              options)
   414          patch_v_s_route(kube_apis.custom_objects,
   415                          v_s_route_setup.route_m.name, v_s_route_setup.route_m.namespace, new_body_m)
   416          patch_v_s_route(kube_apis.custom_objects,
   417                          v_s_route_setup.route_s.name, v_s_route_setup.route_s.namespace, new_body_s)
   418          wait_before_test(1)
   419          ic_pod_name = get_first_pod_name(kube_apis.v1, ingress_controller_prerequisites.namespace)
   420          config = get_vs_nginx_template_conf(kube_apis.v1,
   421                                              v_s_route_setup.namespace,
   422                                              v_s_route_setup.vs_name,
   423                                              ic_pod_name,
   424                                              ingress_controller_prerequisites.namespace)
   425          resp_1 = requests.get(f"{req_url}{v_s_route_setup.route_m.paths[0]}",
   426                                headers={"host": v_s_route_setup.vs_host})
   427          resp_2 = requests.get(f"{req_url}{v_s_route_setup.route_s.paths[0]}",
   428                                headers={"host": v_s_route_setup.vs_host})
   429          vsr_s_events = get_events(kube_apis.v1, v_s_route_setup.route_s.namespace)
   430          vsr_m_events = get_events(kube_apis.v1, v_s_route_setup.route_m.namespace)
   431  
   432          assert_event_count_increased(vsr_m_event_text, initial_count_vsr_m, vsr_m_events)
   433          assert_event_count_increased(vsr_s_event_text, initial_count_vsr_s, vsr_s_events)
   434          for _ in expected_strings:
   435              assert _ in config
   436          assert_response_codes(resp_1, resp_2)
   437  
   438      @pytest.mark.parametrize('options', [{"slow-start": "0s"}])
   439      def test_slow_start_warning(self, kube_apis,
   440                                  ingress_controller_prerequisites, crd_ingress_controller,
   441                                  v_s_route_setup, v_s_route_app_setup, options):
   442          ic_pod_name = get_first_pod_name(kube_apis.v1, ingress_controller_prerequisites.namespace)
   443          text_s = f"{v_s_route_setup.route_s.namespace}/{v_s_route_setup.route_s.name}"
   444          text_m = f"{v_s_route_setup.route_m.namespace}/{v_s_route_setup.route_m.name}"
   445          vsr_s_event_text = f"Configuration for {text_s} was added or updated with warning(s): " \
   446              f"Slow start will be disabled"
   447          vsr_m_event_text = f"Configuration for {text_m} was added or updated with warning(s): " \
   448              f"Slow start will be disabled"
   449          print(f"Case 2: no key in ConfigMap, option specified in VSR")
   450          new_body_m = generate_item_with_upstream_options(
   451              f"{TEST_DATA}/virtual-server-route-upstream-options/route-multiple.yaml",
   452              options)
   453          new_body_s = generate_item_with_upstream_options(
   454              f"{TEST_DATA}/virtual-server-route-upstream-options/route-single.yaml",
   455              options)
   456          patch_v_s_route(kube_apis.custom_objects,
   457                          v_s_route_setup.route_m.name, v_s_route_setup.route_m.namespace, new_body_m)
   458          patch_v_s_route(kube_apis.custom_objects,
   459                          v_s_route_setup.route_s.name, v_s_route_setup.route_s.namespace, new_body_s)
   460          wait_before_test(1)
   461  
   462          config = get_vs_nginx_template_conf(kube_apis.v1,
   463                                              v_s_route_setup.namespace,
   464                                              v_s_route_setup.vs_name,
   465                                              ic_pod_name,
   466                                              ingress_controller_prerequisites.namespace)
   467          vsr_s_events = get_events(kube_apis.v1, v_s_route_setup.route_s.namespace)
   468          vsr_m_events = get_events(kube_apis.v1, v_s_route_setup.route_m.namespace)
   469  
   470          assert_event(vsr_s_event_text, vsr_s_events)
   471          assert_event(vsr_m_event_text, vsr_m_events)
   472          assert "slow_start" not in config
   473  
   474      def test_validation_flow(self, kube_apis, ingress_controller_prerequisites,
   475                               crd_ingress_controller, v_s_route_setup):
   476          invalid_fields_s = [
   477              "upstreams[0].healthCheck.path", "upstreams[0].healthCheck.interval", "upstreams[0].healthCheck.jitter",
   478              "upstreams[0].healthCheck.fails", "upstreams[0].healthCheck.passes",
   479              "upstreams[0].healthCheck.connect-timeout",
   480              "upstreams[0].healthCheck.read-timeout", "upstreams[0].healthCheck.send-timeout",
   481              "upstreams[0].healthCheck.headers[0].name", "upstreams[0].healthCheck.headers[0].value",
   482              "upstreams[0].healthCheck.statusMatch",
   483              "upstreams[0].slow-start",
   484              "upstreams[0].queue.size", "upstreams[0].queue.timeout",
   485              "upstreams[0].sessionCookie.name", "upstreams[0].sessionCookie.path",
   486              "upstreams[0].sessionCookie.expires", "upstreams[0].sessionCookie.domain"
   487          ]
   488          invalid_fields_m = [
   489              "upstreams[0].healthCheck.path", "upstreams[0].healthCheck.interval", "upstreams[0].healthCheck.jitter",
   490              "upstreams[0].healthCheck.fails", "upstreams[0].healthCheck.passes",
   491              "upstreams[0].healthCheck.connect-timeout",
   492              "upstreams[0].healthCheck.read-timeout", "upstreams[0].healthCheck.send-timeout",
   493              "upstreams[0].healthCheck.headers[0].name", "upstreams[0].healthCheck.headers[0].value",
   494              "upstreams[0].healthCheck.statusMatch",
   495              "upstreams[0].slow-start",
   496              "upstreams[0].queue.size", "upstreams[0].queue.timeout",
   497              "upstreams[0].sessionCookie.name", "upstreams[0].sessionCookie.path",
   498              "upstreams[0].sessionCookie.expires", "upstreams[0].sessionCookie.domain",
   499              "upstreams[1].healthCheck.path", "upstreams[1].healthCheck.interval", "upstreams[1].healthCheck.jitter",
   500              "upstreams[1].healthCheck.fails", "upstreams[1].healthCheck.passes",
   501              "upstreams[1].healthCheck.connect-timeout",
   502              "upstreams[1].healthCheck.read-timeout", "upstreams[1].healthCheck.send-timeout",
   503              "upstreams[1].healthCheck.headers[0].name", "upstreams[0].healthCheck.headers[0].value",
   504              "upstreams[1].healthCheck.statusMatch",
   505              "upstreams[1].slow-start",
   506              "upstreams[1].queue.size", "upstreams[1].queue.timeout",
   507              "upstreams[1].sessionCookie.name", "upstreams[1].sessionCookie.path",
   508              "upstreams[1].sessionCookie.expires", "upstreams[1].sessionCookie.domain"
   509          ]
   510          text_s = f"{v_s_route_setup.route_s.namespace}/{v_s_route_setup.route_s.name}"
   511          text_m = f"{v_s_route_setup.route_m.namespace}/{v_s_route_setup.route_m.name}"
   512          vsr_s_event_text = f"VirtualServerRoute {text_s} was rejected with error:"
   513          vsr_m_event_text = f"VirtualServerRoute {text_m} was rejected with error:"
   514          patch_v_s_route_from_yaml(kube_apis.custom_objects,
   515                                    v_s_route_setup.route_s.name,
   516                                    f"{TEST_DATA}/virtual-server-route-upstream-options/plus-route-s-invalid-keys.yaml",
   517                                    v_s_route_setup.route_s.namespace)
   518          patch_v_s_route_from_yaml(kube_apis.custom_objects,
   519                                    v_s_route_setup.route_m.name,
   520                                    f"{TEST_DATA}/virtual-server-route-upstream-options/plus-route-m-invalid-keys.yaml",
   521                                    v_s_route_setup.route_m.namespace)
   522          wait_before_test(2)
   523          ic_pod_name = get_first_pod_name(kube_apis.v1, ingress_controller_prerequisites.namespace)
   524          config = get_vs_nginx_template_conf(kube_apis.v1,
   525                                              v_s_route_setup.namespace,
   526                                              v_s_route_setup.vs_name,
   527                                              ic_pod_name,
   528                                              ingress_controller_prerequisites.namespace)
   529          vsr_s_events = get_events(kube_apis.v1, v_s_route_setup.route_s.namespace)
   530          vsr_m_events = get_events(kube_apis.v1, v_s_route_setup.route_m.namespace)
   531  
   532          assert_event_starts_with_text_and_contains_errors(vsr_s_event_text, vsr_s_events, invalid_fields_s)
   533          assert_event_starts_with_text_and_contains_errors(vsr_m_event_text, vsr_m_events, invalid_fields_m)
   534          assert "upstream" not in config
   535  
   536      def test_openapi_validation_flow(self, kube_apis, ingress_controller_prerequisites,
   537                                       crd_ingress_controller, v_s_route_setup):
   538          ic_pod_name = get_first_pod_name(kube_apis.v1, ingress_controller_prerequisites.namespace)
   539          invalid_fields = [
   540              "upstreams.healthCheck.enable", "upstreams.healthCheck.path",
   541              "upstreams.healthCheck.interval", "upstreams.healthCheck.jitter",
   542              "upstreams.healthCheck.fails", "upstreams.healthCheck.passes",
   543              "upstreams.healthCheck.port", "upstreams.healthCheck.connect-timeout",
   544              "upstreams.healthCheck.read-timeout", "upstreams.healthCheck.send-timeout",
   545              "upstreams.healthCheck.headers.name", "upstreams.healthCheck.headers.value",
   546              "upstreams.healthCheck.statusMatch",
   547              "upstreams.slow-start",
   548              "upstreams.queue.size", "upstreams.queue.timeout",
   549              "upstreams.sessionCookie.name", "upstreams.sessionCookie.path",
   550              "upstreams.sessionCookie.expires", "upstreams.sessionCookie.domain",
   551              "upstreams.sessionCookie.httpOnly", "upstreams.sessionCookie.secure"
   552          ]
   553          config_old = get_vs_nginx_template_conf(kube_apis.v1,
   554                                                  v_s_route_setup.namespace,
   555                                                  v_s_route_setup.vs_name,
   556                                                  ic_pod_name,
   557                                                  ingress_controller_prerequisites.namespace)
   558          source_yaml = f"{TEST_DATA}/virtual-server-route-upstream-options/plus-route-s-invalid-keys-openapi.yaml"
   559          try:
   560              patch_v_s_route_from_yaml(kube_apis.custom_objects,
   561                                        v_s_route_setup.route_s.name,
   562                                        source_yaml,
   563                                        v_s_route_setup.route_s.namespace)
   564          except ApiException as ex:
   565              assert ex.status == 422
   566              for item in invalid_fields:
   567                  assert item in ex.body
   568          except Exception as ex:
   569              pytest.fail(f"An unexpected exception is raised: {ex}")
   570          else:
   571              pytest.fail("Expected an exception but there was none")
   572  
   573          wait_before_test(2)
   574          config_new = get_vs_nginx_template_conf(kube_apis.v1,
   575                                                  v_s_route_setup.namespace,
   576                                                  v_s_route_setup.vs_name,
   577                                                  ic_pod_name,
   578                                                  ingress_controller_prerequisites.namespace)
   579          assert config_old == config_new, "Expected: config doesn't change"