From affa1207d2fd3ea20fc703d89f251e45382d588e Mon Sep 17 00:00:00 2001 From: Qianglin Li Date: Tue, 4 Apr 2023 21:01:02 +0800 Subject: [PATCH] Fix the problem that ignoreUriCase does not work when the path type is prefix (#260) Signed-off-by: charlie --- pkg/ingress/kube/annotations/canary.go | 48 +++--- pkg/ingress/kube/ingress/controller.go | 107 ++++++------- pkg/ingress/kube/ingressv1/controller.go | 109 ++++++------- pkg/ingress/kube/ingressv1/controller_test.go | 37 +++++ .../tests/httproute-canary-header.go | 68 +++++++++ .../tests/httproute-canary-header.yaml | 56 +++++++ ...tch.go => httproute-enable-ignore-case.go} | 42 ++++- ...yaml => httproute-enable-ignore-case.yaml} | 14 ++ .../conformance/tests/httproute-match-path.go | 143 ++++++++++++++++++ .../tests/httproute-match-path.yaml | 53 +++++++ test/ingress/e2e_test.go | 3 +- 11 files changed, 551 insertions(+), 129 deletions(-) rename test/ingress/conformance/tests/{httproute-ignore-case-match.go => httproute-enable-ignore-case.go} (67%) rename test/ingress/conformance/tests/{httproute-ignore-case-match.yaml => httproute-enable-ignore-case.yaml} (76%) create mode 100644 test/ingress/conformance/tests/httproute-match-path.go create mode 100644 test/ingress/conformance/tests/httproute-match-path.yaml diff --git a/pkg/ingress/kube/annotations/canary.go b/pkg/ingress/kube/annotations/canary.go index 7b6f4b5ef..ea39004ab 100644 --- a/pkg/ingress/kube/annotations/canary.go +++ b/pkg/ingress/kube/annotations/canary.go @@ -134,37 +134,41 @@ func ApplyByHeader(canary, route *networking.HTTPRoute, canaryIngress *Ingress) // Modified match base on by header if canaryConfig.Header != "" { - canary.Match[0].Headers = map[string]*networking.StringMatch{ - canaryConfig.Header: { - MatchType: &networking.StringMatch_Exact{ - Exact: "always", - }, - }, - } - if canaryConfig.HeaderValue != "" { - canary.Match[0].Headers = map[string]*networking.StringMatch{ + for _, match := range canary.Match { + match.Headers = map[string]*networking.StringMatch{ canaryConfig.Header: { - MatchType: &networking.StringMatch_Regex{ - Regex: "always|" + canaryConfig.HeaderValue, + MatchType: &networking.StringMatch_Exact{ + Exact: "always", }, }, } - } else if canaryConfig.HeaderPattern != "" { - canary.Match[0].Headers = map[string]*networking.StringMatch{ - canaryConfig.Header: { - MatchType: &networking.StringMatch_Regex{ - Regex: canaryConfig.HeaderPattern, + if canaryConfig.HeaderValue != "" { + match.Headers = map[string]*networking.StringMatch{ + canaryConfig.Header: { + MatchType: &networking.StringMatch_Regex{ + Regex: "always|" + canaryConfig.HeaderValue, + }, }, - }, + } + } else if canaryConfig.HeaderPattern != "" { + match.Headers = map[string]*networking.StringMatch{ + canaryConfig.Header: { + MatchType: &networking.StringMatch_Regex{ + Regex: canaryConfig.HeaderPattern, + }, + }, + } } } } else if canaryConfig.Cookie != "" { - canary.Match[0].Headers = map[string]*networking.StringMatch{ - "cookie": { - MatchType: &networking.StringMatch_Regex{ - Regex: "^(.\\*?;)?(" + canaryConfig.Cookie + "=always)(;.\\*)?$", + for _, match := range canary.Match { + match.Headers = map[string]*networking.StringMatch{ + "cookie": { + MatchType: &networking.StringMatch_Regex{ + Regex: "^(.\\*?;)?(" + canaryConfig.Cookie + "=always)(;.\\*)?$", + }, }, - }, + } } } diff --git a/pkg/ingress/kube/ingress/controller.go b/pkg/ingress/kube/ingress/controller.go index 19bbc7871..3d5999c69 100644 --- a/pkg/ingress/kube/ingress/controller.go +++ b/pkg/ingress/kube/ingress/controller.go @@ -19,7 +19,6 @@ import ( "fmt" "path" "reflect" - "regexp" "sort" "strings" "sync" @@ -532,40 +531,25 @@ func (c *controller) ConvertHTTPRoute(convertOptions *common.ConvertOptions, wra Host: rule.Host, ClusterId: c.options.ClusterId, } - httpMatch := &networking.HTTPMatchRequest{} - path := httpPath.Path + var pathType common.PathType + originPath := httpPath.Path if wrapper.AnnotationsConfig.NeedRegexMatch() { - wrapperHttpRoute.OriginPathType = common.Regex - httpMatch.Uri = &networking.StringMatch{ - MatchType: &networking.StringMatch_Regex{Regex: httpPath.Path + ".*"}, - } + pathType = common.Regex } else { switch *httpPath.PathType { case ingress.PathTypeExact: - wrapperHttpRoute.OriginPathType = common.Exact - httpMatch.Uri = &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: httpPath.Path}, - } + pathType = common.Exact case ingress.PathTypePrefix: - wrapperHttpRoute.OriginPathType = common.Prefix - // borrow from implement of official istio code. - if path == "/" { - wrapperVS.ConfiguredDefaultBackend = true - // Optimize common case of / to not needed regex - httpMatch.Uri = &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: path}, - } - } else { - path = strings.TrimSuffix(path, "/") - httpMatch.Uri = &networking.StringMatch{ - MatchType: &networking.StringMatch_Regex{Regex: regexp.QuoteMeta(path) + common.PrefixMatchRegex}, - } + pathType = common.Prefix + if httpPath.Path != "/" { + originPath = strings.TrimSuffix(httpPath.Path, "/") } } } - wrapperHttpRoute.OriginPath = path - wrapperHttpRoute.HTTPRoute.Match = []*networking.HTTPMatchRequest{httpMatch} + wrapperHttpRoute.OriginPath = originPath + wrapperHttpRoute.OriginPathType = pathType + wrapperHttpRoute.HTTPRoute.Match = c.generateHttpMatches(pathType, httpPath.Path, wrapperVS) wrapperHttpRoute.HTTPRoute.Name = common.GenerateUniqueRouteName(c.options.SystemNamespace, wrapperHttpRoute) ingressRouteBuilder := convertOptions.IngressRouteCache.New(wrapperHttpRoute) @@ -748,46 +732,31 @@ func (c *controller) ApplyCanaryIngress(convertOptions *common.ConvertOptions, w } for _, httpPath := range rule.HTTP.Paths { - path := httpPath.Path - canary := &common.WrapperHTTPRoute{ HTTPRoute: &networking.HTTPRoute{}, WrapperConfig: wrapper, Host: rule.Host, ClusterId: c.options.ClusterId, } - httpMatch := &networking.HTTPMatchRequest{} + var pathType common.PathType + originPath := httpPath.Path if wrapper.AnnotationsConfig.NeedRegexMatch() { - canary.OriginPathType = common.Regex - httpMatch.Uri = &networking.StringMatch{ - MatchType: &networking.StringMatch_Regex{Regex: httpPath.Path + ".*"}, - } + pathType = common.Regex } else { switch *httpPath.PathType { case ingress.PathTypeExact: - canary.OriginPathType = common.Exact - httpMatch.Uri = &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: httpPath.Path}, - } + pathType = common.Exact case ingress.PathTypePrefix: - canary.OriginPathType = common.Prefix - // borrow from implement of official istio code. - if path == "/" { - // Optimize common case of / to not needed regex - httpMatch.Uri = &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: path}, - } - } else { - path = strings.TrimSuffix(path, "/") - httpMatch.Uri = &networking.StringMatch{ - MatchType: &networking.StringMatch_Regex{Regex: regexp.QuoteMeta(path) + common.PrefixMatchRegex}, - } + pathType = common.Prefix + if httpPath.Path != "/" { + originPath = strings.TrimSuffix(httpPath.Path, "/") } } } - canary.OriginPath = path - canary.HTTPRoute.Match = []*networking.HTTPMatchRequest{httpMatch} + canary.OriginPath = originPath + canary.OriginPathType = pathType + canary.HTTPRoute.Match = c.generateHttpMatches(pathType, httpPath.Path, nil) canary.HTTPRoute.Name = common.GenerateUniqueRouteName(c.options.SystemNamespace, canary) ingressRouteBuilder := convertOptions.IngressRouteCache.New(canary) @@ -1180,6 +1149,42 @@ func (c *controller) shouldProcessIngressUpdate(ing *ingress.Ingress) (bool, err return preProcessed, nil } +func (c *controller) generateHttpMatches(pathType common.PathType, path string, wrapperVS *common.WrapperVirtualService) []*networking.HTTPMatchRequest { + var httpMatches []*networking.HTTPMatchRequest + + httpMatch := &networking.HTTPMatchRequest{} + switch pathType { + case common.Regex: + httpMatch.Uri = &networking.StringMatch{ + MatchType: &networking.StringMatch_Regex{Regex: path + ".*"}, + } + case common.Exact: + httpMatch.Uri = &networking.StringMatch{ + MatchType: &networking.StringMatch_Exact{Exact: path}, + } + case common.Prefix: + if path == "/" { + if wrapperVS != nil { + wrapperVS.ConfiguredDefaultBackend = true + } + // Optimize common case of / to not needed regex + httpMatch.Uri = &networking.StringMatch{ + MatchType: &networking.StringMatch_Prefix{Prefix: path}, + } + } else { + newPath := strings.TrimSuffix(path, "/") + httpMatches = append(httpMatches, c.generateHttpMatches(common.Exact, newPath, wrapperVS)...) + httpMatch.Uri = &networking.StringMatch{ + MatchType: &networking.StringMatch_Prefix{Prefix: newPath + "/"}, + } + } + } + + httpMatches = append(httpMatches, httpMatch) + + return httpMatches +} + // setDefaultMSEIngressOptionalField sets a default value for optional fields when is not defined. func setDefaultMSEIngressOptionalField(ing *ingress.Ingress) { if ing == nil { diff --git a/pkg/ingress/kube/ingressv1/controller.go b/pkg/ingress/kube/ingressv1/controller.go index cd04caaa8..595e78b91 100644 --- a/pkg/ingress/kube/ingressv1/controller.go +++ b/pkg/ingress/kube/ingressv1/controller.go @@ -19,7 +19,6 @@ import ( "fmt" "path" "reflect" - "regexp" "sort" "strings" "sync" @@ -507,6 +506,7 @@ func (c *controller) ConvertHTTPRoute(convertOptions *common.ConvertOptions, wra } wrapperHttpRoutes := make([]*common.WrapperHTTPRoute, 0, len(rule.HTTP.Paths)) + for _, httpPath := range rule.HTTP.Paths { wrapperHttpRoute := &common.WrapperHTTPRoute{ HTTPRoute: &networking.HTTPRoute{}, @@ -514,40 +514,25 @@ func (c *controller) ConvertHTTPRoute(convertOptions *common.ConvertOptions, wra Host: rule.Host, ClusterId: c.options.ClusterId, } - httpMatch := &networking.HTTPMatchRequest{} - path := httpPath.Path + var pathType common.PathType + originPath := httpPath.Path if wrapper.AnnotationsConfig.NeedRegexMatch() { - wrapperHttpRoute.OriginPathType = common.Regex - httpMatch.Uri = &networking.StringMatch{ - MatchType: &networking.StringMatch_Regex{Regex: httpPath.Path + ".*"}, - } + pathType = common.Regex } else { switch *httpPath.PathType { case ingress.PathTypeExact: - wrapperHttpRoute.OriginPathType = common.Exact - httpMatch.Uri = &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: httpPath.Path}, - } + pathType = common.Exact case ingress.PathTypePrefix: - wrapperHttpRoute.OriginPathType = common.Prefix - // borrow from implement of official istio code. - if path == "/" { - wrapperVS.ConfiguredDefaultBackend = true - // Optimize common case of / to not needed regex - httpMatch.Uri = &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: path}, - } - } else { - path = strings.TrimSuffix(path, "/") - httpMatch.Uri = &networking.StringMatch{ - MatchType: &networking.StringMatch_Regex{Regex: regexp.QuoteMeta(path) + common.PrefixMatchRegex}, - } + pathType = common.Prefix + if httpPath.Path != "/" { + originPath = strings.TrimSuffix(httpPath.Path, "/") } } } - wrapperHttpRoute.OriginPath = path - wrapperHttpRoute.HTTPRoute.Match = []*networking.HTTPMatchRequest{httpMatch} + wrapperHttpRoute.OriginPath = originPath + wrapperHttpRoute.OriginPathType = pathType + wrapperHttpRoute.HTTPRoute.Match = c.generateHttpMatches(pathType, httpPath.Path, wrapperVS) wrapperHttpRoute.HTTPRoute.Name = common.GenerateUniqueRouteName(c.options.SystemNamespace, wrapperHttpRoute) ingressRouteBuilder := convertOptions.IngressRouteCache.New(wrapperHttpRoute) @@ -620,6 +605,42 @@ func (c *controller) ConvertHTTPRoute(convertOptions *common.ConvertOptions, wra return nil } +func (c *controller) generateHttpMatches(pathType common.PathType, path string, wrapperVS *common.WrapperVirtualService) []*networking.HTTPMatchRequest { + var httpMatches []*networking.HTTPMatchRequest + + httpMatch := &networking.HTTPMatchRequest{} + switch pathType { + case common.Regex: + httpMatch.Uri = &networking.StringMatch{ + MatchType: &networking.StringMatch_Regex{Regex: path + ".*"}, + } + case common.Exact: + httpMatch.Uri = &networking.StringMatch{ + MatchType: &networking.StringMatch_Exact{Exact: path}, + } + case common.Prefix: + if path == "/" { + if wrapperVS != nil { + wrapperVS.ConfiguredDefaultBackend = true + } + // Optimize common case of / to not needed regex + httpMatch.Uri = &networking.StringMatch{ + MatchType: &networking.StringMatch_Prefix{Prefix: path}, + } + } else { + newPath := strings.TrimSuffix(path, "/") + httpMatches = append(httpMatches, c.generateHttpMatches(common.Exact, newPath, wrapperVS)...) + httpMatch.Uri = &networking.StringMatch{ + MatchType: &networking.StringMatch_Prefix{Prefix: newPath + "/"}, + } + } + } + + httpMatches = append(httpMatches, httpMatch) + + return httpMatches +} + func (c *controller) ApplyDefaultBackend(convertOptions *common.ConvertOptions, wrapper *common.WrapperConfig) error { if wrapper.AnnotationsConfig.IsCanary() { return nil @@ -717,46 +738,31 @@ func (c *controller) ApplyCanaryIngress(convertOptions *common.ConvertOptions, w } for _, httpPath := range rule.HTTP.Paths { - path := httpPath.Path - canary := &common.WrapperHTTPRoute{ HTTPRoute: &networking.HTTPRoute{}, WrapperConfig: wrapper, Host: rule.Host, ClusterId: c.options.ClusterId, } - httpMatch := &networking.HTTPMatchRequest{} + var pathType common.PathType + originPath := httpPath.Path if wrapper.AnnotationsConfig.NeedRegexMatch() { - canary.OriginPathType = common.Regex - httpMatch.Uri = &networking.StringMatch{ - MatchType: &networking.StringMatch_Regex{Regex: httpPath.Path + ".*"}, - } + pathType = common.Regex } else { switch *httpPath.PathType { case ingress.PathTypeExact: - canary.OriginPathType = common.Exact - httpMatch.Uri = &networking.StringMatch{ - MatchType: &networking.StringMatch_Exact{Exact: httpPath.Path}, - } + pathType = common.Exact case ingress.PathTypePrefix: - canary.OriginPathType = common.Prefix - // borrow from implement of official istio code. - if path == "/" { - // Optimize common case of / to not needed regex - httpMatch.Uri = &networking.StringMatch{ - MatchType: &networking.StringMatch_Prefix{Prefix: path}, - } - } else { - path = strings.TrimSuffix(path, "/") - httpMatch.Uri = &networking.StringMatch{ - MatchType: &networking.StringMatch_Regex{Regex: regexp.QuoteMeta(path) + common.PrefixMatchRegex}, - } + pathType = common.Prefix + if httpPath.Path != "/" { + originPath = strings.TrimSuffix(httpPath.Path, "/") } } } - canary.OriginPath = path - canary.HTTPRoute.Match = []*networking.HTTPMatchRequest{httpMatch} + canary.OriginPath = originPath + canary.OriginPathType = pathType + canary.HTTPRoute.Match = c.generateHttpMatches(pathType, httpPath.Path, nil) canary.HTTPRoute.Name = common.GenerateUniqueRouteName(c.options.SystemNamespace, canary) ingressRouteBuilder := convertOptions.IngressRouteCache.New(canary) @@ -819,6 +825,7 @@ func (c *controller) ApplyCanaryIngress(convertOptions *common.ConvertOptions, w } else { convertOptions.IngressRouteCache.Update(targetRoute) } + } } return nil diff --git a/pkg/ingress/kube/ingressv1/controller_test.go b/pkg/ingress/kube/ingressv1/controller_test.go index 3fd3107ea..5930c2e0e 100644 --- a/pkg/ingress/kube/ingressv1/controller_test.go +++ b/pkg/ingress/kube/ingressv1/controller_test.go @@ -17,6 +17,8 @@ package ingressv1 import ( "testing" + "github.com/google/go-cmp/cmp" + networking "istio.io/api/networking/v1alpha3" v1 "k8s.io/api/networking/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -76,3 +78,38 @@ func TestShouldProcessIngressUpdate(t *testing.T) { t.Fatal("should be true") } } + +func TestGenerateHttpMatches(t *testing.T) { + c := controller{} + + tt := []struct { + pathType common.PathType + path string + expect []*networking.HTTPMatchRequest + }{ + { + pathType: common.Prefix, + path: "/foo", + expect: []*networking.HTTPMatchRequest{ + { + Uri: &networking.StringMatch{ + MatchType: &networking.StringMatch_Exact{Exact: "/foo"}, + }, + }, { + Uri: &networking.StringMatch{ + MatchType: &networking.StringMatch_Prefix{Prefix: "/foo/"}, + }, + }, + }, + }, + } + + for _, testcase := range tt { + httpMatches := c.generateHttpMatches(testcase.pathType, testcase.path, nil) + for idx, httpMatch := range httpMatches { + if diff := cmp.Diff(httpMatch, testcase.expect[idx]); diff != "" { + t.Errorf("generateHttpMatches() mismatch (-want +got):\n%s", diff) + } + } + } +} diff --git a/test/ingress/conformance/tests/httproute-canary-header.go b/test/ingress/conformance/tests/httproute-canary-header.go index c41708ee0..7957a8798 100644 --- a/test/ingress/conformance/tests/httproute-canary-header.go +++ b/test/ingress/conformance/tests/httproute-canary-header.go @@ -64,6 +64,74 @@ var HTTPRouteCanaryHeader = suite.ConformanceTest{ StatusCode: 200, }, }, + }, { + Meta: http.AssertionMeta{ + TargetBackend: "infra-backend-v2", + TargetNamespace: "higress-conformance-infra", + }, + Request: http.AssertionRequest{ + ActualRequest: http.Request{ + Path: "/foo", + Host: "canary.higress.io", + Headers: map[string]string{"traffic-split-higress": "true"}, + }, + }, + Response: http.AssertionResponse{ + ExpectedResponse: http.Response{ + StatusCode: 200, + }, + }, + }, { + Meta: http.AssertionMeta{ + TargetBackend: "infra-backend-v1", + TargetNamespace: "higress-conformance-infra", + }, + Request: http.AssertionRequest{ + ActualRequest: http.Request{ + Path: "/foo/bar", + Host: "canary.higress.io", + Headers: map[string]string{"traffic-split-higress": "true"}, + }, + }, + Response: http.AssertionResponse{ + ExpectedResponse: http.Response{ + StatusCode: 200, + }, + }, + }, + { + Meta: http.AssertionMeta{ + TargetBackend: "infra-backend-v3", + TargetNamespace: "higress-conformance-infra", + }, + Request: http.AssertionRequest{ + ActualRequest: http.Request{ + Path: "/foo", + Host: "canary.higress.io", + }, + }, + Response: http.AssertionResponse{ + ExpectedResponse: http.Response{ + StatusCode: 200, + }, + }, + }, + { + Meta: http.AssertionMeta{ + TargetBackend: "infra-backend-v3", + TargetNamespace: "higress-conformance-infra", + }, + Request: http.AssertionRequest{ + ActualRequest: http.Request{ + Path: "/foo/bar", + Host: "canary.higress.io", + }, + }, + Response: http.AssertionResponse{ + ExpectedResponse: http.Response{ + StatusCode: 200, + }, + }, }, } diff --git a/test/ingress/conformance/tests/httproute-canary-header.yaml b/test/ingress/conformance/tests/httproute-canary-header.yaml index 5d0c4bbb8..f20ddc09c 100644 --- a/test/ingress/conformance/tests/httproute-canary-header.yaml +++ b/test/ingress/conformance/tests/httproute-canary-header.yaml @@ -53,3 +53,59 @@ spec: name: infra-backend-v2 port: number: 8080 +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + annotations: + nginx.ingress.kubernetes.io/canary: "true" + nginx.ingress.kubernetes.io/canary-by-header: "traffic-split-higress" + nginx.ingress.kubernetes.io/canary-by-header-value: "true" + name: ingress-echo-canary-value-prefix + namespace: higress-conformance-infra +spec: + ingressClassName: higress + rules: + - host: canary.higress.io + http: + paths: + - path: /foo + pathType: Exact + backend: + service: + name: infra-backend-v2 + port: + number: 8080 + - path: /foo + pathType: Prefix + backend: + service: + name: infra-backend-v1 + port: + number: 8080 +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: ingress-echo-prefix + namespace: higress-conformance-infra +spec: + ingressClassName: higress + rules: + - host: canary.higress.io + http: + paths: + - path: /foo + pathType: Exact + backend: + service: + name: infra-backend-v3 + port: + number: 8080 + - path: /foo + pathType: Prefix + backend: + service: + name: infra-backend-v3 + port: + number: 8080 diff --git a/test/ingress/conformance/tests/httproute-ignore-case-match.go b/test/ingress/conformance/tests/httproute-enable-ignore-case.go similarity index 67% rename from test/ingress/conformance/tests/httproute-ignore-case-match.go rename to test/ingress/conformance/tests/httproute-enable-ignore-case.go index 37e03a92c..c3855c518 100644 --- a/test/ingress/conformance/tests/httproute-ignore-case-match.go +++ b/test/ingress/conformance/tests/httproute-enable-ignore-case.go @@ -22,13 +22,13 @@ import ( ) func init() { - HigressConformanceTests = append(HigressConformanceTests, HTTPRouteIgnoreCaseMatch) + HigressConformanceTests = append(HigressConformanceTests, HTTPRouteEnableIgnoreCase) } -var HTTPRouteIgnoreCaseMatch = suite.ConformanceTest{ - ShortName: "HTTPRouteIgnoreCaseMatch", +var HTTPRouteEnableIgnoreCase = suite.ConformanceTest{ + ShortName: "HTTPRouteEnableIgnoreCase", Description: "A Ingress in the higress-conformance-infra namespace that ignores URI case in HTTP match.", - Manifests: []string{"tests/httproute-ignore-case-match.yaml"}, + Manifests: []string{"tests/httproute-enable-ignore-case.yaml"}, Test: func(t *testing.T, suite *suite.ConformanceTestSuite) { testcases := []http.Assertion{ { @@ -65,6 +65,40 @@ var HTTPRouteIgnoreCaseMatch = suite.ConformanceTest{ StatusCode: 200, }, }, + }, { + Meta: http.AssertionMeta{ + TestCaseName: "case3: enable ignoreCase", + TargetBackend: "infra-backend-v2", + TargetNamespace: "higress-conformance-infra", + }, + Request: http.AssertionRequest{ + ActualRequest: http.Request{ + Path: "/BAR", + Host: "foo.com", + }, + }, + Response: http.AssertionResponse{ + ExpectedResponse: http.Response{ + StatusCode: 200, + }, + }, + }, { + Meta: http.AssertionMeta{ + TestCaseName: "case4: enable ignoreCase", + TargetBackend: "infra-backend-v3", + TargetNamespace: "higress-conformance-infra", + }, + Request: http.AssertionRequest{ + ActualRequest: http.Request{ + Path: "/CAT/ok", + Host: "foo.com", + }, + }, + Response: http.AssertionResponse{ + ExpectedResponse: http.Response{ + StatusCode: 200, + }, + }, }, } diff --git a/test/ingress/conformance/tests/httproute-ignore-case-match.yaml b/test/ingress/conformance/tests/httproute-enable-ignore-case.yaml similarity index 76% rename from test/ingress/conformance/tests/httproute-ignore-case-match.yaml rename to test/ingress/conformance/tests/httproute-enable-ignore-case.yaml index 432010222..07d0227bc 100644 --- a/test/ingress/conformance/tests/httproute-ignore-case-match.yaml +++ b/test/ingress/conformance/tests/httproute-enable-ignore-case.yaml @@ -32,3 +32,17 @@ spec: name: infra-backend-v1 port: number: 8080 + - pathType: Prefix + path: "/bar" + backend: + service: + name: infra-backend-v2 + port: + number: 8080 + - pathType: Prefix + path: "/cat/" + backend: + service: + name: infra-backend-v3 + port: + number: 8080 diff --git a/test/ingress/conformance/tests/httproute-match-path.go b/test/ingress/conformance/tests/httproute-match-path.go new file mode 100644 index 000000000..dc9ca1f03 --- /dev/null +++ b/test/ingress/conformance/tests/httproute-match-path.go @@ -0,0 +1,143 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// 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 tests + +import ( + "testing" + + "github.com/alibaba/higress/test/ingress/conformance/utils/http" + "github.com/alibaba/higress/test/ingress/conformance/utils/suite" +) + +func init() { + HigressConformanceTests = append(HigressConformanceTests, HTTPRouteMatchPath) +} + +var HTTPRouteMatchPath = suite.ConformanceTest{ + ShortName: "HTTPRouteMatchPath", + Description: "A Ingress in the higress-conformance-infra namespace that match different path.", + Manifests: []string{"tests/httproute-match-path.yaml"}, + Test: func(t *testing.T, suite *suite.ConformanceTestSuite) { + testcases := []http.Assertion{ + { + Meta: http.AssertionMeta{ + TestCaseName: "case1: normal request", + TargetBackend: "infra-backend-v1", + TargetNamespace: "higress-conformance-infra", + }, + Request: http.AssertionRequest{ + ActualRequest: http.Request{ + Path: "/foo", + Host: "foo.com", + }, + }, + Response: http.AssertionResponse{ + ExpectedResponse: http.Response{ + StatusCode: 200, + }, + }, + }, { + Meta: http.AssertionMeta{ + TestCaseName: "case2: path is '/bar' and match prefix path successfully", + TargetBackend: "infra-backend-v2", + TargetNamespace: "higress-conformance-infra", + }, + Request: http.AssertionRequest{ + ActualRequest: http.Request{ + Path: "/bar", + Host: "foo.com", + }, + }, + Response: http.AssertionResponse{ + ExpectedResponse: http.Response{ + StatusCode: 200, + }, + }, + }, { + Meta: http.AssertionMeta{ + TestCaseName: "case2: path is '/bar' and match prefix path failed", + }, + Request: http.AssertionRequest{ + ActualRequest: http.Request{ + Path: "/bard", + Host: "foo.com", + }, + }, + Response: http.AssertionResponse{ + ExpectedResponse: http.Response{ + StatusCode: 404, + }, + }, + }, { + Meta: http.AssertionMeta{ + TestCaseName: "case3: path is '/cat/' and match prefix path successfully", + TargetBackend: "infra-backend-v3", + TargetNamespace: "higress-conformance-infra", + }, + Request: http.AssertionRequest{ + ActualRequest: http.Request{ + Path: "/cat", + Host: "foo.com", + }, + }, + Response: http.AssertionResponse{ + ExpectedResponse: http.Response{ + StatusCode: 200, + }, + }, + }, { + Meta: http.AssertionMeta{ + TestCaseName: "case4: path is '/cat/' and match prefix path successfully", + TargetBackend: "infra-backend-v3", + TargetNamespace: "higress-conformance-infra", + }, + Request: http.AssertionRequest{ + ActualRequest: http.Request{ + Path: "/cat/ok", + Host: "foo.com", + }, + }, + Response: http.AssertionResponse{ + ExpectedResponse: http.Response{ + StatusCode: 200, + }, + }, + }, { + Meta: http.AssertionMeta{ + TargetBackend: "infra-backend-v2", + TargetNamespace: "higress-conformance-infra", + }, + Request: http.AssertionRequest{ + ActualRequest: http.Request{ + Path: "/foo/", + Host: "foo.com", + }, + }, + Response: http.AssertionResponse{ + ExpectedResponse: http.Response{ + StatusCode: 200, + }, + }, + }, + } + + t.Run("HTTPRoute Match different path Cases", func(t *testing.T) { + for _, testcase := range testcases { + http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, suite.GatewayAddress, testcase) + } + }) + + }, +} diff --git a/test/ingress/conformance/tests/httproute-match-path.yaml b/test/ingress/conformance/tests/httproute-match-path.yaml new file mode 100644 index 000000000..227a6e012 --- /dev/null +++ b/test/ingress/conformance/tests/httproute-match-path.yaml @@ -0,0 +1,53 @@ +# Copyright (c) 2022 Alibaba Group Holding Ltd. +# +# 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. + +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: httproute-match-path + namespace: higress-conformance-infra +spec: + ingressClassName: higress + rules: + - host: "foo.com" + http: + paths: + - pathType: Prefix + path: "/foo" + backend: + service: + name: infra-backend-v2 + port: + number: 8080 + - pathType: Exact + path: "/foo" + backend: + service: + name: infra-backend-v1 + port: + number: 8080 + - pathType: Prefix + path: "/bar" + backend: + service: + name: infra-backend-v2 + port: + number: 8080 + - pathType: Prefix + path: "/cat/" + backend: + service: + name: infra-backend-v3 + port: + number: 8080 diff --git a/test/ingress/e2e_test.go b/test/ingress/e2e_test.go index b7672069b..91478e27a 100644 --- a/test/ingress/e2e_test.go +++ b/test/ingress/e2e_test.go @@ -57,7 +57,7 @@ func TestHigressConformanceTests(t *testing.T) { tests.HTTPRouteRewriteHost, tests.HTTPRouteCanaryHeader, tests.HTTPRouteEnableCors, - tests.HTTPRouteIgnoreCaseMatch, + tests.HTTPRouteEnableIgnoreCase, tests.HTTPRouteMatchMethods, tests.HTTPRouteMatchQueryParams, tests.HTTPRouteMatchHeaders, @@ -69,6 +69,7 @@ func TestHigressConformanceTests(t *testing.T) { tests.HTTPRouteCanaryHeaderWithCustomizedHeader, tests.HTTPRouteWhitelistSourceRange, tests.HTTPRouteCanaryWeight, + tests.HTTPRouteMatchPath, } cSuite.Run(t, higressTests)