From bd7d65f40ecb861b33b3fb9ee4e1301b1cd87b61 Mon Sep 17 00:00:00 2001 From: Megrez Lu Date: Tue, 14 May 2024 11:55:15 +0800 Subject: [PATCH] add gateway api tests Signed-off-by: Megrez Lu --- api/v1beta1/rollout_types.go | 1 + pkg/trafficrouting/network/gateway/gateway.go | 32 ++- .../network/gateway/gateway_test.go | 248 ++++++++++++++++++ pkg/util/slices_utils.go | 24 ++ 4 files changed, 297 insertions(+), 8 deletions(-) create mode 100644 pkg/util/slices_utils.go diff --git a/api/v1beta1/rollout_types.go b/api/v1beta1/rollout_types.go index 7ac371f1..a90e99aa 100644 --- a/api/v1beta1/rollout_types.go +++ b/api/v1beta1/rollout_types.go @@ -159,6 +159,7 @@ type HttpRouteMatch struct { // Path specifies a HTTP request path matcher. // Supported list: // - Istio: https://istio.io/latest/docs/reference/config/networking/virtual-service/#HTTPMatchRequest + // - GatewayAPI: If path is defined, the whole HttpRouteMatch will be used as a standalone matcher // // +optional Path *gatewayv1beta1.HTTPPathMatch `json:"path,omitempty"` diff --git a/pkg/trafficrouting/network/gateway/gateway.go b/pkg/trafficrouting/network/gateway/gateway.go index b401d33d..31d5d97c 100644 --- a/pkg/trafficrouting/network/gateway/gateway.go +++ b/pkg/trafficrouting/network/gateway/gateway.go @@ -155,7 +155,13 @@ func (r *gatewayController) buildDesiredHTTPRoute(rules []gatewayv1beta1.HTTPRou func (r *gatewayController) buildCanaryHeaderHttpRoutes(rules []gatewayv1beta1.HTTPRouteRule, matches []v1beta1.HttpRouteMatch) []gatewayv1beta1.HTTPRouteRule { var desired []gatewayv1beta1.HTTPRouteRule - var canarys []gatewayv1beta1.HTTPRouteRule + var canaries []gatewayv1beta1.HTTPRouteRule + pathMatches := util.Filter(matches, func(match v1beta1.HttpRouteMatch) bool { + return match.Path != nil + }) + nonPathMatches := util.Filter(matches, func(match v1beta1.HttpRouteMatch) bool { + return match.Path == nil + }) for i := range rules { rule := rules[i] if _, canaryRef := getServiceBackendRef(rule, r.conf.CanaryService); canaryRef != nil { @@ -172,25 +178,35 @@ func (r *gatewayController) buildCanaryHeaderHttpRoutes(rules []gatewayv1beta1.H canaryRule.BackendRefs = []gatewayv1beta1.HTTPBackendRef{*canaryRef} // set canary headers in httpRoute var newMatches []gatewayv1beta1.HTTPRouteMatch + for _, pathMatch := range pathMatches { + newMatches = append(newMatches, gatewayv1beta1.HTTPRouteMatch{ + Path: pathMatch.Path, + Headers: pathMatch.Headers, + QueryParams: pathMatch.QueryParams, + }) + } + // reset pathMatches + pathMatches = nil + if len(nonPathMatches) == 0 && len(newMatches) == 0 { + continue + } for j := range canaryRule.Matches { canaryRuleMatch := &canaryRule.Matches[j] - for k := range matches { + for k := range nonPathMatches { canaryRuleMatchBase := *canaryRuleMatch - // TODO: shall we check Path equality? if len(matches[k].Headers) > 0 { canaryRuleMatchBase.Headers = append(canaryRuleMatchBase.Headers, matches[k].Headers...) - } else if matches[k].Path != nil { - canaryRuleMatchBase.Path = matches[k].Path - } else if len(matches[k].QueryParams) > 0 { + } + if len(matches[k].QueryParams) > 0 { canaryRuleMatchBase.QueryParams = append(canaryRuleMatchBase.QueryParams, matches[k].QueryParams...) } newMatches = append(newMatches, canaryRuleMatchBase) } } canaryRule.Matches = newMatches - canarys = append(canarys, *canaryRule) + canaries = append(canaries, *canaryRule) } - desired = append(desired, canarys...) + desired = append(desired, canaries...) return desired } diff --git a/pkg/trafficrouting/network/gateway/gateway_test.go b/pkg/trafficrouting/network/gateway/gateway_test.go index 776cd271..648b6c0e 100644 --- a/pkg/trafficrouting/network/gateway/gateway_test.go +++ b/pkg/trafficrouting/network/gateway/gateway_test.go @@ -491,6 +491,254 @@ func TestBuildDesiredHTTPRoute(t *testing.T) { return rules }, }, + { + name: "test query params and headers", + getRouteRules: func() []gatewayv1beta1.HTTPRouteRule { + rules := routeDemo.DeepCopy().Spec.Rules + return rules + }, + getRoutes: func() (*int32, []v1beta1.HttpRouteMatch) { + iQueryParamType := gatewayv1beta1.QueryParamMatchRegularExpression + iHeaderType := gatewayv1beta1.HeaderMatchRegularExpression + return nil, []v1beta1.HttpRouteMatch{ + // queryParams + headers + { + QueryParams: []gatewayv1beta1.HTTPQueryParamMatch{ + { + Name: "user_id", + Value: "123*", + Type: &iQueryParamType, + }, + { + Name: "canary", + Value: "true", + }, + }, + Headers: []gatewayv1beta1.HTTPHeaderMatch{ + { + Name: "user_id", + Value: "123*", + Type: &iHeaderType, + }, + { + Name: "canary", + Value: "true", + }, + }, + }, + } + }, + desiredRules: func() []gatewayv1beta1.HTTPRouteRule { + rules := routeDemo.DeepCopy().Spec.Rules + iQueryParamType := gatewayv1beta1.QueryParamMatchRegularExpression + iHeaderType := gatewayv1beta1.HeaderMatchRegularExpression + rules = append(rules, gatewayv1beta1.HTTPRouteRule{ + Matches: []gatewayv1beta1.HTTPRouteMatch{ + { + Path: &gatewayv1beta1.HTTPPathMatch{ + Value: utilpointer.String("/store"), + }, + Headers: []gatewayv1beta1.HTTPHeaderMatch{ + { + Name: "version", + Value: "v2", + }, + { + Name: "user_id", + Value: "123*", + Type: &iHeaderType, + }, + { + Name: "canary", + Value: "true", + }, + }, + QueryParams: []gatewayv1beta1.HTTPQueryParamMatch{ + { + Name: "user_id", + Value: "123*", + Type: &iQueryParamType, + }, + { + Name: "canary", + Value: "true", + }, + }, + }, + { + Path: &gatewayv1beta1.HTTPPathMatch{ + Value: utilpointer.String("/v2/store"), + }, + Headers: []gatewayv1beta1.HTTPHeaderMatch{ + { + Name: "user_id", + Value: "123*", + Type: &iHeaderType, + }, + { + Name: "canary", + Value: "true", + }, + }, + QueryParams: []gatewayv1beta1.HTTPQueryParamMatch{ + { + Name: "user_id", + Value: "123*", + Type: &iQueryParamType, + }, + { + Name: "canary", + Value: "true", + }, + }, + }, + }, + BackendRefs: []gatewayv1beta1.HTTPBackendRef{ + { + BackendRef: gatewayv1beta1.BackendRef{ + BackendObjectReference: gatewayv1beta1.BackendObjectReference{ + Kind: &kindSvc, + Name: "store-svc-canary", + Port: &portNum, + }, + }, + }, + }, + }) + rules = append(rules, gatewayv1beta1.HTTPRouteRule{ + Matches: []gatewayv1beta1.HTTPRouteMatch{ + { + Path: &gatewayv1beta1.HTTPPathMatch{ + Value: utilpointer.String("/storage"), + }, + Headers: []gatewayv1beta1.HTTPHeaderMatch{ + { + Name: "user_id", + Value: "123*", + Type: &iHeaderType, + }, + { + Name: "canary", + Value: "true", + }, + }, + QueryParams: []gatewayv1beta1.HTTPQueryParamMatch{ + { + Name: "user_id", + Value: "123*", + Type: &iQueryParamType, + }, + { + Name: "canary", + Value: "true", + }, + }, + }, + }, + BackendRefs: []gatewayv1beta1.HTTPBackendRef{ + { + BackendRef: gatewayv1beta1.BackendRef{ + BackendObjectReference: gatewayv1beta1.BackendObjectReference{ + Kind: &kindSvc, + Name: "store-svc-canary", + Port: &portNum, + }, + }, + }, + }, + }) + return rules + }, + }, + { + name: "test path replace", + getRouteRules: func() []gatewayv1beta1.HTTPRouteRule { + rules := routeDemo.DeepCopy().Spec.Rules + return rules + }, + getRoutes: func() (*int32, []v1beta1.HttpRouteMatch) { + iQueryParamType := gatewayv1beta1.QueryParamMatchRegularExpression + iHeaderType := gatewayv1beta1.HeaderMatchRegularExpression + return nil, []v1beta1.HttpRouteMatch{ + // queryParams + headers + path + { + QueryParams: []gatewayv1beta1.HTTPQueryParamMatch{ + { + Name: "user_id", + Value: "123*", + Type: &iQueryParamType, + }, + { + Name: "canary", + Value: "true", + }, + }, + Headers: []gatewayv1beta1.HTTPHeaderMatch{ + { + Name: "user_id", + Value: "123*", + Type: &iHeaderType, + }, + { + Name: "canary", + Value: "true", + }, + }, + Path: &gatewayv1beta1.HTTPPathMatch{ + Value: utilpointer.String("/storage/v2"), + }, + }, + } + }, + desiredRules: func() []gatewayv1beta1.HTTPRouteRule { + rules := routeDemo.DeepCopy().Spec.Rules + iQueryParamType := gatewayv1beta1.QueryParamMatchRegularExpression + iHeaderType := gatewayv1beta1.HeaderMatchRegularExpression + rules = append(rules, gatewayv1beta1.HTTPRouteRule{ + Matches: []gatewayv1beta1.HTTPRouteMatch{ + { + Path: &gatewayv1beta1.HTTPPathMatch{ + Value: utilpointer.String("/storage/v2"), + }, + Headers: []gatewayv1beta1.HTTPHeaderMatch{ + { + Name: "user_id", + Value: "123*", + Type: &iHeaderType, + }, + { + Name: "canary", + Value: "true", + }, + }, + QueryParams: []gatewayv1beta1.HTTPQueryParamMatch{ + { + Name: "user_id", + Value: "123*", + Type: &iQueryParamType, + }, + { + Name: "canary", + Value: "true", + }, + }, + }, + }, + BackendRefs: []gatewayv1beta1.HTTPBackendRef{ + { + BackendRef: gatewayv1beta1.BackendRef{ + BackendObjectReference: gatewayv1beta1.BackendObjectReference{ + Kind: &kindSvc, + Name: "store-svc-canary", + Port: &portNum, + }, + }, + }, + }, + }) + return rules + }, + }, { name: "canary weight: 20", getRouteRules: func() []gatewayv1beta1.HTTPRouteRule { diff --git a/pkg/util/slices_utils.go b/pkg/util/slices_utils.go new file mode 100644 index 00000000..66004542 --- /dev/null +++ b/pkg/util/slices_utils.go @@ -0,0 +1,24 @@ +/* +Copyright 2022 The Kruise Authors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package util + +func Filter[E any](s []E, f func(E) bool) []E { + s2 := make([]E, 0, len(s)) + for _, e := range s { + if f(e) { + s2 = append(s2, e) + } + } + return s2 +}