From 321bdf5e254e6a4bc9ae6dbbb649b12720fd8699 Mon Sep 17 00:00:00 2001 From: Eguzki Astiz Lezaun Date: Fri, 14 Jun 2024 16:31:06 +0200 Subject: [PATCH] rate limit policy: header match support in wasm configuration (#698) * rate limit policy: header match support in wasm configuration * fix rebase issues --- pkg/rlptools/wasm/utils.go | 23 ++++++++++- pkg/rlptools/wasm/utils_test.go | 72 +++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/pkg/rlptools/wasm/utils.go b/pkg/rlptools/wasm/utils.go index 787c01b83..a190aed6a 100644 --- a/pkg/rlptools/wasm/utils.go +++ b/pkg/rlptools/wasm/utils.go @@ -186,15 +186,25 @@ func conditionsFromRule(rule gatewayapiv1.HTTPRouteRule, hostnames []gatewayapiv func patternExpresionsFromMatch(match gatewayapiv1.HTTPRouteMatch) []PatternExpression { expressions := make([]PatternExpression, 0) + // path if match.Path != nil { expressions = append(expressions, patternExpresionFromPathMatch(*match.Path)) } + // method if match.Method != nil { expressions = append(expressions, patternExpresionFromMethod(*match.Method)) } - // TODO(eastizle): only paths and methods implemented + // headers + for _, headerMatch := range match.Headers { + // Multiple match values are ANDed together + expressions = append(expressions, patternExpresionFromHeader(headerMatch)) + } + + // TODO(eguzki): query params. Investigate integration with wasm regarding Envoy params + // from https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/advanced/attributes + // request.query -> string : The query portion of the URL in the format of “name1=value1&name2=value2”. return expressions } @@ -230,6 +240,17 @@ func patternExpresionFromMethod(method gatewayapiv1.HTTPMethod) PatternExpressio } } +func patternExpresionFromHeader(headerMatch gatewayapiv1.HTTPHeaderMatch) PatternExpression { + // As for gateway api v1, the only operation type with core support is Exact match. + // https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPHeaderMatch + + return PatternExpression{ + Selector: kuadrantv1beta2.ContextSelector(fmt.Sprintf("request.headers.%s", headerMatch.Name)), + Operator: PatternOperator(kuadrantv1beta2.EqualOperator), + Value: headerMatch.Value, + } +} + func patternExpresionFromHostname(hostname gatewayapiv1.Hostname) PatternExpression { value := string(hostname) operator := "eq" diff --git a/pkg/rlptools/wasm/utils_test.go b/pkg/rlptools/wasm/utils_test.go index a6485143a..cffb26bfc 100644 --- a/pkg/rlptools/wasm/utils_test.go +++ b/pkg/rlptools/wasm/utils_test.go @@ -9,6 +9,7 @@ import ( "github.com/google/go-cmp/cmp" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/ptr" gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1" kuadrantv1beta2 "github.com/kuadrant/kuadrant-operator/api/v1beta2" @@ -327,6 +328,77 @@ func TestRules(t *testing.T) { }, }, }, + { + name: "Route with header match", + rlp: rlp("my-rlp", map[string]kuadrantv1beta2.Limit{ + "50rps": { + Rates: []kuadrantv1beta2.Rate{counter50rps}, + }, + }), + route: &gatewayapiv1.HTTPRoute{ + Spec: gatewayapiv1.HTTPRouteSpec{ + Hostnames: []gatewayapiv1.Hostname{"*.example.com"}, + Rules: []gatewayapiv1.HTTPRouteRule{ + { + Matches: []gatewayapiv1.HTTPRouteMatch{ + { + Path: &gatewayapiv1.HTTPPathMatch{ + Type: ptr.To(gatewayapiv1.PathMatchPathPrefix), + Value: ptr.To("/v1"), + }, + Method: ptr.To(gatewayapiv1.HTTPMethodGet), + Headers: []gatewayapiv1.HTTPHeaderMatch{ + { + Name: gatewayapiv1.HTTPHeaderName("X-kuadrant-a"), + Value: "1", + }, + { + Name: gatewayapiv1.HTTPHeaderName("X-kuadrant-b"), + Value: "1", + }, + }, + }, + }, + }, + }, + }, + }, + expectedRules: []Rule{ + { + Conditions: []Condition{ + { + AllOf: []PatternExpression{ + { + Selector: "request.url_path", + Operator: PatternOperator(kuadrantv1beta2.StartsWithOperator), + Value: "/v1", + }, + { + Selector: "request.method", + Operator: PatternOperator(kuadrantv1beta2.EqualOperator), + Value: "GET", + }, + { + Selector: "request.headers.X-kuadrant-a", + Operator: PatternOperator(kuadrantv1beta2.EqualOperator), + Value: "1", + }, + { + Selector: "request.headers.X-kuadrant-b", + Operator: PatternOperator(kuadrantv1beta2.EqualOperator), + Value: "1", + }, + }, + }, + }, + Data: []DataItem{ + { + Static: &StaticSpec{Key: "limit.50rps__783b9343", Value: "1"}, + }, + }, + }, + }, + }, } for _, tc := range testCases {