envoyproxy / envoy

Cloud-native high-performance edge/middle/service proxy
https://www.envoyproxy.io
Apache License 2.0
24.26k stars 4.68k forks source link

List of matchers for route #34866

Open Ferdudas97 opened 1 week ago

Ferdudas97 commented 1 week ago

List of matchers for route: Route should have list of matchers instead of one match

Description: Current api forces Istio (and other control planes) to create many duplicated routes with different matchers but the same route actions. Having possibility to specify list of match rules (OR operation between all rules) would simplify a envoy config and reduce used memory.

Istio Example

Route config from Istio virtual service

  - match:
    - headers:
        x-service-tag-preference:
          regex: (^|.*\|)vte7($|\|.*)
      withoutHeaders:
        x-service-tag: {}
    - headers:
        x-service-tag:
          exact: fast
        x-service-tag-preference:
          regex: (^|.*\|)vte7($|\|.*)
    - headers:
        x-service-tag:
          exact: vte7
        x-service-tag-preference:
          regex: (^|.*\|)vte7($|\|.*)
    route:
    - destination:
        host: foo.bar.svc.cluster.local
        port:
          number: 31011
      weight: 50

produces 3 routes, with only different matchers

  {
          "match": {
           "prefix": "/",
           "case_sensitive": true,
           "headers": [
            {
             "name": "x-service-tag",
             "present_match": true,
             "invert_match": true
            },
            {
             "name": "x-service-tag-preference",
             "string_match": {
              "safe_regex": {
               "regex": "(^|.*\\|)vte7($|\\|.*)"
              }
             }
            }
           ]
          },
          "route": {
           "cluster": "outbound|31011||foo.bar.svc.cluster.local",
           "timeout": "0s",
           "retry_policy": {
            "retry_on": "connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes",
            "num_retries": 2,
            "retry_host_predicate": [
             {
              "name": "envoy.retry_host_predicates.previous_hosts",
              "typed_config": {
               "@type": "type.googleapis.com/envoy.extensions.retry.host.previous_hosts.v3.PreviousHostsPredicate"
              }
             }
            ],
            "host_selection_retry_max_attempts": "5",
            "retriable_status_codes": [
             503
            ]
           },
           "max_grpc_timeout": "0s",
           "host_rewrite_header": "x-envoy-original-host"
          },
          "metadata": {
           "filter_metadata": {
            "istio": {
             "config": "/apis/networking.istio.io/v1alpha3/namespaces/default/virtual-service/foo"
            }
           }
          },
          "decorator": {
           "operation": "foo.bar.svc.cluster.local:31011/*"
          }
         },
         {
          "match": {
           "prefix": "/",
           "case_sensitive": true,
           "headers": [
            {
             "name": "x-service-tag",
             "string_match": {
              "exact": "fast"
             }
            },
            {
             "name": "x-service-tag-preference",
             "string_match": {
              "safe_regex": {
               "regex": "(^|.*\\|)vte7($|\\|.*)"
              }
             }
            }
           ]
          },
          "route": {
           "cluster": "outbound|31011||foo.bar.svc.cluster.local",
           "timeout": "0s",
           "retry_policy": {
            "retry_on": "connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes",
            "num_retries": 2,
            "retry_host_predicate": [
             {
              "name": "envoy.retry_host_predicates.previous_hosts",
              "typed_config": {
               "@type": "type.googleapis.com/envoy.extensions.retry.host.previous_hosts.v3.PreviousHostsPredicate"
              }
             }
            ],
            "host_selection_retry_max_attempts": "5",
            "retriable_status_codes": [
             503
            ]
           },
           "max_grpc_timeout": "0s",
           "host_rewrite_header": "x-envoy-original-host"
          },
          "metadata": {
           "filter_metadata": {
            "istio": {
             "config": "/apis/networking.istio.io/v1alpha3/namespaces/default/virtual-service/foo"
            }
           }
          },
          "decorator": {
           "operation": "foo.bar.svc.cluster.local:31011/*"
          }
         },
         {
          "match": {
           "prefix": "/",
           "case_sensitive": true,
           "headers": [
            {
             "name": "x-service-tag",
             "string_match": {
              "exact": "vte7"
             }
            },
            {
             "name": "x-service-tag-preference",
             "string_match": {
              "safe_regex": {
               "regex": "(^|.*\\|)vte7($|\\|.*)"
              }
             }
            }
           ]
          },
          "route": {
           "cluster": "outbound|31011||foo.bar.svc.cluster.local",
           "timeout": "0s",
           "retry_policy": {
            "retry_on": "connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes",
            "num_retries": 2,
            "retry_host_predicate": [
             {
              "name": "envoy.retry_host_predicates.previous_hosts",
              "typed_config": {
               "@type": "type.googleapis.com/envoy.extensions.retry.host.previous_hosts.v3.PreviousHostsPredicate"
              }
             }
            ],
            "host_selection_retry_max_attempts": "5",
            "retriable_status_codes": [
             503
            ]
           },
           "max_grpc_timeout": "0s",
           "host_rewrite_header": "x-envoy-original-host"
          },
          "metadata": {
           "filter_metadata": {
            "istio": {
             "config": "/apis/networking.istio.io/v1alpha3/namespaces/default/virtual-service/foo"
            }
           }
          },
          "decorator": {
           "operation": "foo.bar.svc.cluster.local:31011/*"
          }
         },
mattklein123 commented 1 week ago

cc @ravenblackx @kyessenov I'm not sure the current state of the art of enhanced matching but would that help here in any way?

ravenblackx commented 6 days ago

I think a MatcherTree can help with this, but it's a bit tricky to generate because at the base level you ~must have prefix/exact path matches for the tree, and some routes may want to e.g. use a regular expression for the path, which means you have to roll all those ones into a sub-matcher on the "no match" branch of the base tree.

There is definitely or_matcher in the Predicate message, so it should be feasible to do the sort of matching that's needed (and I guess you don't have to use a MatcherTree at the base, a MatcherList should more closely mimic the pre-tree behavior while still allowing custom predicates.)