You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by zh...@apache.org on 2022/11/19 09:48:46 UTC

[apisix-ingress-controller] branch master updated: feat: support variable in ApisixRoute exprs scope (#1466)

This is an automated email from the ASF dual-hosted git repository.

zhangjintao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix-ingress-controller.git


The following commit(s) were added to refs/heads/master by this push:
     new e6eb3bf2 feat: support variable in ApisixRoute exprs scope (#1466)
e6eb3bf2 is described below

commit e6eb3bf2aed4c39e91271e4f74b61ff6fe97ad16
Author: Xin Rong <al...@gmail.com>
AuthorDate: Sat Nov 19 17:48:41 2022 +0800

    feat: support variable in ApisixRoute exprs scope (#1466)
    
    Co-authored-by: Jintao Zhang <zh...@gmail.com>
---
 pkg/kube/apisix/const/const.go                   |   2 +
 pkg/providers/apisix/translation/apisix_route.go |  16 +-
 samples/deploy/crd/v1/ApisixRoute.yaml           |   3 +
 test/e2e/scaffold/scaffold.go                    |   4 +
 test/e2e/suite-features/route_match_exprs.go     | 299 +++++++++++++++++++++++
 5 files changed, 314 insertions(+), 10 deletions(-)

diff --git a/pkg/kube/apisix/const/const.go b/pkg/kube/apisix/const/const.go
index f972267b..19666c40 100644
--- a/pkg/kube/apisix/const/const.go
+++ b/pkg/kube/apisix/const/const.go
@@ -49,4 +49,6 @@ const (
 	ScopePath = "Path"
 	// ScopeCookie means the route match expression subject is in cookie.
 	ScopeCookie = "Cookie"
+	// ScopeVariable means the route match expression subject is in variable.
+	ScopeVariable = "Variable"
 )
diff --git a/pkg/providers/apisix/translation/apisix_route.go b/pkg/providers/apisix/translation/apisix_route.go
index 2fd8681a..f49fab4d 100644
--- a/pkg/providers/apisix/translation/apisix_route.go
+++ b/pkg/providers/apisix/translation/apisix_route.go
@@ -489,6 +489,8 @@ func (t *translator) TranslateRouteMatchExprs(nginxVars []configv2.ApisixRouteHT
 			subj = "cookie_" + expr.Subject.Name
 		case _const.ScopePath:
 			subj = "uri"
+		case _const.ScopeVariable:
+			subj = expr.Subject.Name
 		default:
 			return nil, errors.New("bad subject name")
 		}
@@ -504,20 +506,14 @@ func (t *translator) TranslateRouteMatchExprs(nginxVars []configv2.ApisixRouteHT
 			op = "=="
 		case _const.OpGreaterThan:
 			op = ">"
-		// TODO Implement "<=", ">=" operators after the
-		// lua-resty-expr supports it. See
-		// https://github.com/api7/lua-resty-expr/issues/28
-		// for details.
-		//case configv2alpha1.OpGreaterThanEqual:
-		//	invert = true
-		//	op = "<"
+		case _const.OpGreaterThanEqual:
+			op = ">="
 		case _const.OpIn:
 			op = "in"
 		case _const.OpLessThan:
 			op = "<"
-		//case configv2alpha1.OpLessThanEqual:
-		//	invert = true
-		//	op = ">"
+		case _const.OpLessThanEqual:
+			op = "<="
 		case _const.OpNotEqual:
 			op = "~="
 		case _const.OpNotIn:
diff --git a/samples/deploy/crd/v1/ApisixRoute.yaml b/samples/deploy/crd/v1/ApisixRoute.yaml
index 8ca97dc9..89dac1de 100644
--- a/samples/deploy/crd/v1/ApisixRoute.yaml
+++ b/samples/deploy/crd/v1/ApisixRoute.yaml
@@ -420,6 +420,7 @@ spec:
                                         - "Header"
                                         - "Path"
                                         - "Query"
+                                        - "Variable"
                                     name:
                                       type: string
                                       minLength: 1
@@ -431,7 +432,9 @@ spec:
                                     - Equal
                                     - NotEqual
                                     - GreaterThan
+                                    - GreaterThanEqual
                                     - LessThan
+                                    - LessThanEqual
                                     - In
                                     - NotIn
                                     - RegexMatch
diff --git a/test/e2e/scaffold/scaffold.go b/test/e2e/scaffold/scaffold.go
index c3c83262..1db6e8e2 100644
--- a/test/e2e/scaffold/scaffold.go
+++ b/test/e2e/scaffold/scaffold.go
@@ -653,6 +653,10 @@ func (s *Scaffold) CreateVersionedApisixResourceWithNamespace(yml, namespace str
 	return fmt.Errorf("the resource %s does not support", kindValue)
 }
 
+func (s *Scaffold) ApisixResourceVersion() string {
+	return s.opts.ApisixResourceVersion
+}
+
 func ApisixResourceVersion() *apisixResourceVersionInfo {
 	return apisixResourceVersion
 }
diff --git a/test/e2e/suite-features/route_match_exprs.go b/test/e2e/suite-features/route_match_exprs.go
index c04003b7..da59e60d 100644
--- a/test/e2e/suite-features/route_match_exprs.go
+++ b/test/e2e/suite-features/route_match_exprs.go
@@ -187,6 +187,75 @@ spec:
 			assert.Contains(ginkgo.GinkgoT(), msg, "404 Route Not Found")
 		})
 
+		ginkgo.It("operator is GreaterThanEqual", func() {
+			if s.ApisixResourceVersion() == scaffold.ApisixResourceVersion().V2beta3 {
+				ginkgo.Skip("Not support ApisixRoute v2beta3")
+			}
+
+			backendSvc, backendPorts := s.DefaultHTTPBackend()
+
+			ar := fmt.Sprintf(`
+apiVersion: apisix.apache.org/v2
+kind: ApisixRoute
+metadata:
+ name: httpbin-route
+spec:
+ http:
+ - name: rule1
+   match:
+     hosts:
+     - httpbin.org
+     paths:
+       - /ip
+     exprs:
+     - subject:
+         scope: Query
+         name: id
+       op: GreaterThanEqual
+       value: "13"
+   backends:
+   - serviceName: %s
+     servicePort: %d
+`, backendSvc, backendPorts[0])
+
+			assert.Nil(ginkgo.GinkgoT(), s.CreateVersionedApisixResource(ar))
+
+			time.Sleep(6 * time.Second)
+			err := s.EnsureNumApisixRoutesCreated(1)
+			assert.Nil(ginkgo.GinkgoT(), err, "Checking number of routes")
+			err = s.EnsureNumApisixUpstreamsCreated(1)
+			assert.Nil(ginkgo.GinkgoT(), err, "Checking number of upstreams")
+
+			_ = s.NewAPISIXClient().GET("/ip").
+				WithHeader("Host", "httpbin.org").
+				WithQuery("id", 100).
+				Expect().
+				Status(http.StatusOK)
+
+			_ = s.NewAPISIXClient().GET("/ip").
+				WithHeader("Host", "httpbin.org").
+				WithQuery("id", 13).
+				Expect().
+				Status(http.StatusOK)
+
+			msg := s.NewAPISIXClient().GET("/ip").
+				WithHeader("Host", "httpbin.org").
+				WithQuery("id", 10).
+				Expect().
+				Status(http.StatusNotFound).
+				Body().
+				Raw()
+			assert.Contains(ginkgo.GinkgoT(), msg, "404 Route Not Found")
+
+			msg = s.NewAPISIXClient().GET("/ip").
+				WithHeader("Host", "httpbin.org").
+				Expect().
+				Status(http.StatusNotFound).
+				Body().
+				Raw()
+			assert.Contains(ginkgo.GinkgoT(), msg, "404 Route Not Found")
+		})
+
 		ginkgo.It("operator is less_than", func() {
 			backendSvc, backendPorts := s.DefaultHTTPBackend()
 
@@ -246,6 +315,75 @@ spec:
 			assert.Contains(ginkgo.GinkgoT(), msg, "404 Route Not Found")
 		})
 
+		ginkgo.It("operator is LessThanEqual", func() {
+			if s.ApisixResourceVersion() == scaffold.ApisixResourceVersion().V2beta3 {
+				ginkgo.Skip("Not support ApisixRoute v2beta3")
+			}
+
+			backendSvc, backendPorts := s.DefaultHTTPBackend()
+
+			ar := fmt.Sprintf(`
+apiVersion: apisix.apache.org/v2
+kind: ApisixRoute
+metadata:
+ name: httpbin-route
+spec:
+ http:
+ - name: rule1
+   match:
+     hosts:
+     - httpbin.org
+     paths:
+       - /ip
+     exprs:
+     - subject:
+         scope: Query
+         name: ID
+       op: LessThanEqual
+       value: "13"
+   backends:
+   - serviceName: %s
+     servicePort: %d
+`, backendSvc, backendPorts[0])
+
+			assert.Nil(ginkgo.GinkgoT(), s.CreateVersionedApisixResource(ar))
+
+			time.Sleep(6 * time.Second)
+			err := s.EnsureNumApisixRoutesCreated(1)
+			assert.Nil(ginkgo.GinkgoT(), err, "Checking number of routes")
+			err = s.EnsureNumApisixUpstreamsCreated(1)
+			assert.Nil(ginkgo.GinkgoT(), err, "Checking number of upstreams")
+
+			_ = s.NewAPISIXClient().GET("/ip").
+				WithHeader("Host", "httpbin.org").
+				WithQuery("ID", 12).
+				Expect().
+				Status(http.StatusOK)
+
+			_ = s.NewAPISIXClient().GET("/ip").
+				WithHeader("Host", "httpbin.org").
+				WithQuery("ID", 13).
+				Expect().
+				Status(http.StatusOK)
+
+			msg := s.NewAPISIXClient().GET("/ip").
+				WithHeader("Host", "httpbin.org").
+				WithQuery("ID", 14).
+				Expect().
+				Status(http.StatusNotFound).
+				Body().
+				Raw()
+			assert.Contains(ginkgo.GinkgoT(), msg, "404 Route Not Found")
+
+			msg = s.NewAPISIXClient().GET("/ip").
+				WithHeader("Host", "httpbin.org").
+				Expect().
+				Status(http.StatusNotFound).
+				Body().
+				Raw()
+			assert.Contains(ginkgo.GinkgoT(), msg, "404 Route Not Found")
+		})
+
 		ginkgo.It("operator is in", func() {
 			backendSvc, backendPorts := s.DefaultHTTPBackend()
 
@@ -683,3 +821,164 @@ spec:
 		suites(scaffold.NewDefaultV2Scaffold)
 	})
 })
+
+var _ = ginkgo.Describe("suite-features: route match exprs with variable", func() {
+	s := scaffold.NewDefaultScaffold()
+	ginkgo.It("exprs with request_method variable", func() {
+		backendSvc, backendPorts := s.DefaultHTTPBackend()
+
+		ar := fmt.Sprintf(`
+apiVersion: apisix.apache.org/v2
+kind: ApisixRoute
+metadata:
+ name: httpbin-route
+spec:
+ http:
+ - name: rule1
+   match:
+     hosts:
+     - httpbin.org
+     paths:
+       - /get
+       - /post
+       - /put
+     exprs:
+     - subject:
+         scope: Variable
+         name: request_method
+       op: In
+       set:
+       - GET
+       - POST
+   backends:
+   - serviceName: %s
+     servicePort: %d
+`, backendSvc, backendPorts[0])
+
+		assert.Nil(ginkgo.GinkgoT(), s.CreateVersionedApisixResource(ar), "creating route")
+		time.Sleep(6 * time.Second)
+		assert.Nil(ginkgo.GinkgoT(), s.EnsureNumApisixRoutesCreated(1), "Checking number of routes")
+
+		_ = s.NewAPISIXClient().GET("/get").
+			WithHeader("Host", "httpbin.org").
+			Expect().
+			Status(http.StatusOK)
+
+		_ = s.NewAPISIXClient().POST("/post").
+			WithHeader("Host", "httpbin.org").
+			Expect().
+			Status(http.StatusOK)
+
+		_ = s.NewAPISIXClient().PUT("/put").
+			WithHeader("Host", "httpbin.org").
+			Expect().
+			Status(http.StatusNotFound)
+	})
+
+	ginkgo.It("exprs with host variable", func() {
+		backendSvc, backendPorts := s.DefaultHTTPBackend()
+
+		ar := fmt.Sprintf(`
+apiVersion: apisix.apache.org/v2
+kind: ApisixRoute
+metadata:
+ name: httpbin-route
+spec:
+ http:
+ - name: rule1
+   match:
+     paths:
+       - /ip
+     exprs:
+     - subject:
+         scope: Variable
+         name: host
+       op: In
+       set:
+       - httpbin.net
+       - httpbin.org
+       - httpbin.com
+   backends:
+   - serviceName: %s
+     servicePort: %d
+`, backendSvc, backendPorts[0])
+
+		assert.Nil(ginkgo.GinkgoT(), s.CreateVersionedApisixResource(ar), "creating route")
+		time.Sleep(6 * time.Second)
+		assert.Nil(ginkgo.GinkgoT(), s.EnsureNumApisixRoutesCreated(1), "Checking number of routes")
+
+		_ = s.NewAPISIXClient().GET("/ip").
+			WithHeader("Host", "httpbin.net").
+			Expect().
+			Status(http.StatusOK)
+
+		_ = s.NewAPISIXClient().GET("/ip").
+			WithHeader("Host", "httpbin.org").
+			Expect().
+			Status(http.StatusOK)
+
+		_ = s.NewAPISIXClient().GET("/ip").
+			WithHeader("Host", "httpbin.com").
+			Expect().
+			Status(http.StatusOK)
+	})
+
+	ginkgo.It("exprs request_method and host variable", func() {
+		backendSvc, backendPorts := s.DefaultHTTPBackend()
+
+		ar := fmt.Sprintf(`
+apiVersion: apisix.apache.org/v2
+kind: ApisixRoute
+metadata:
+ name: httpbin-route
+spec:
+ http:
+ - name: rule1
+   match:
+     paths:
+       - /*
+     exprs:
+     - subject:
+         scope: Variable
+         name: request_method
+       op: In
+       set:
+       - GET
+       - PUT
+     - subject:
+         scope: Variable
+         name: host
+       op: In
+       set:
+       - httpbin.org
+       - httpbin.com
+   backends:
+   - serviceName: %s
+     servicePort: %d
+`, backendSvc, backendPorts[0])
+
+		assert.Nil(ginkgo.GinkgoT(), s.CreateVersionedApisixResource(ar), "creating route")
+		time.Sleep(6 * time.Second)
+		assert.Nil(ginkgo.GinkgoT(), s.EnsureNumApisixRoutesCreated(1), "Checking number of routes")
+
+		_ = s.NewAPISIXClient().GET("/get").
+			WithHeader("Host", "httpbin.net").
+			Expect().
+			Status(http.StatusNotFound)
+
+		_ = s.NewAPISIXClient().GET("/get").
+			WithHeader("Host", "httpbin.org").
+			Expect().
+			Status(http.StatusOK)
+
+		_ = s.NewAPISIXClient().POST("/post").
+			WithHeader("Host", "httpbin.org").
+			Expect().
+			Status(http.StatusNotFound)
+
+		_ = s.NewAPISIXClient().PUT("/put").
+			WithHeader("Host", "httpbin.com").
+			Expect().
+			Status(http.StatusOK)
+	})
+})