smi-spec
smi-spec copied to clipboard
Can HttpRouteGroup header filters be made to match either absent or a specific value?
Great to see header filters added to the SMI spec! I had a question, which I don't think has been addressed so far (apologies if I missed it)...
Take, for example, the HTTPRouteGroup
specified in https://github.com/deislabs/smi-spec/blob/master/traffic-split.md:
kind: HTTPRouteGroup
metadata:
name: ab-test
matches:
- name: firefox-users
headers:
- user-agent: ".*Firefox.*"
This matches if the User-Agent header is present and contains "Firefox".
I'd like to be able to express "either User-Agent is absent or (if present) it contains 'Firefox'", e.g. because my default is to assume Firefox.
Would a regular expression that matches blank too (e.g. user-agent: "(.*Firefox.*)?"
) be sufficient for this, or would this only match if the User-Agent header was present and blank, but not if the User-Agent header was absent?
(I appreciate the User-Agent header might not be a great example here - I'm actually thinking of a different header specific to my own use case.)
Apologies if this is the wrong place for questions. I would have asked on Slack, but I didn't know how to contact the workspace administrator for an invitation.
AFAIK, we don't have a good way to check for existence right now. I'd love to know a little bit more about your use case.
Thanks! In terms of use case...
I have ~10 backend services and a similar number of composite services that front them, each fanning out requests to the appropriate backend service(s).
The composite services are each Kubernetes Deployments.
Each of the backend services is either a single Kubernetes Deployment or multiple Kubernetes Deployments, running the same image but with different configuration. For example, I might have different deployments for different service-levels (Platinum, Gold, Silver, Bronze), regions (UK, FR, DE), customers and/or combinations of the above.
When the composite service issues a request, it optionally specifies the service-level, region and/or customer in HTTP headers, e.g. service-level: gold
and customer: alice
. If it leaves an HTTP header absent, it indicates no preference.
I'd like to use an SMI-managed service mesh to route HTTP requests to the backend service deployment that best matches the specified service-level, region and customer.
I hope that helps - please let me know if there are any clever ways of achieving what I'm trying to do. One solution might be to specify an explicit wildcard (e.g. *
) in the HTTP header, but I'm trying to avoid changing the API.
Why can't the composite service just modify the path or hostname?
The simple answer is probably just that I'm trying not to have to change the API! This might not be a good reason, though, so, exploring some other approaches...
Let's imagine that we have the following rules for which deployment (fronted by a service) to use:
Service-Level | Region | Customer | Service |
---|---|---|---|
platinum | * | alice | alice |
platinum | FR | * | platinum |
gold | UK | * | gold |
* | bob | bob | |
DE | * | de | |
* | * | default |
We could move the function to do this lookup into the composite service, so that the hostname the composite service issued the request to was that of the deployment. However, that feels a bit awkward - it feels like the right place for this policy to live is in a service mesh rather than in all the clients.
Alternatively, we could encode the service-level, region and customer into the hostname, e.g. platinum-any-alice
or gold-uk-any
, and then have the service mesh map each of these to the correct service using regular expressions. This feels a bit verbose - the hostnames are already quite long and if I need to add another criterion in future (e.g. something for canarying), it ends up longer still.
We could encode it in the path, e.g. /service-levels/platinum/regions/any/customers/alice/my-path
. Again, this is quite verbose and ends up getting longer if/when we need to add more criteria.
Encoding it as request-URIs might be a bit more elegant, e.g. /my-path?service-level=platinum&customer=alice
. This feels like the best choice after headers.
I'm not sure I see the difference between adding a header or picking a hostname in your application code. In fact, the added complexity of "if X or Y" inside a HTTPRouteGroup
is still not sitting well with me. That said, the spec allows for the header routing today.
One way to implement this whole thing would be to make it all work with multiple TrafficSplit
definitions, one for each part of the matrix. That would be the way you'd have to do it anyways, with the added benefit of routing decisions being more explicit.