Skip to content

Commit e6eb3bf

Browse files
feat: support variable in ApisixRoute exprs scope (#1466)
Co-authored-by: Jintao Zhang <[email protected]>
1 parent be7edf6 commit e6eb3bf

File tree

5 files changed

+314
-10
lines changed

5 files changed

+314
-10
lines changed

pkg/kube/apisix/const/const.go

+2
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,6 @@ const (
4949
ScopePath = "Path"
5050
// ScopeCookie means the route match expression subject is in cookie.
5151
ScopeCookie = "Cookie"
52+
// ScopeVariable means the route match expression subject is in variable.
53+
ScopeVariable = "Variable"
5254
)

pkg/providers/apisix/translation/apisix_route.go

+6-10
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,8 @@ func (t *translator) TranslateRouteMatchExprs(nginxVars []configv2.ApisixRouteHT
489489
subj = "cookie_" + expr.Subject.Name
490490
case _const.ScopePath:
491491
subj = "uri"
492+
case _const.ScopeVariable:
493+
subj = expr.Subject.Name
492494
default:
493495
return nil, errors.New("bad subject name")
494496
}
@@ -504,20 +506,14 @@ func (t *translator) TranslateRouteMatchExprs(nginxVars []configv2.ApisixRouteHT
504506
op = "=="
505507
case _const.OpGreaterThan:
506508
op = ">"
507-
// TODO Implement "<=", ">=" operators after the
508-
// lua-resty-expr supports it. See
509-
// https://github.com/api7/lua-resty-expr/issues/28
510-
// for details.
511-
//case configv2alpha1.OpGreaterThanEqual:
512-
// invert = true
513-
// op = "<"
509+
case _const.OpGreaterThanEqual:
510+
op = ">="
514511
case _const.OpIn:
515512
op = "in"
516513
case _const.OpLessThan:
517514
op = "<"
518-
//case configv2alpha1.OpLessThanEqual:
519-
// invert = true
520-
// op = ">"
515+
case _const.OpLessThanEqual:
516+
op = "<="
521517
case _const.OpNotEqual:
522518
op = "~="
523519
case _const.OpNotIn:

samples/deploy/crd/v1/ApisixRoute.yaml

+3
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,7 @@ spec:
420420
- "Header"
421421
- "Path"
422422
- "Query"
423+
- "Variable"
423424
name:
424425
type: string
425426
minLength: 1
@@ -431,7 +432,9 @@ spec:
431432
- Equal
432433
- NotEqual
433434
- GreaterThan
435+
- GreaterThanEqual
434436
- LessThan
437+
- LessThanEqual
435438
- In
436439
- NotIn
437440
- RegexMatch

test/e2e/scaffold/scaffold.go

+4
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,10 @@ func (s *Scaffold) CreateVersionedApisixResourceWithNamespace(yml, namespace str
653653
return fmt.Errorf("the resource %s does not support", kindValue)
654654
}
655655

656+
func (s *Scaffold) ApisixResourceVersion() string {
657+
return s.opts.ApisixResourceVersion
658+
}
659+
656660
func ApisixResourceVersion() *apisixResourceVersionInfo {
657661
return apisixResourceVersion
658662
}

test/e2e/suite-features/route_match_exprs.go

+299
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,75 @@ spec:
187187
assert.Contains(ginkgo.GinkgoT(), msg, "404 Route Not Found")
188188
})
189189

190+
ginkgo.It("operator is GreaterThanEqual", func() {
191+
if s.ApisixResourceVersion() == scaffold.ApisixResourceVersion().V2beta3 {
192+
ginkgo.Skip("Not support ApisixRoute v2beta3")
193+
}
194+
195+
backendSvc, backendPorts := s.DefaultHTTPBackend()
196+
197+
ar := fmt.Sprintf(`
198+
apiVersion: apisix.apache.org/v2
199+
kind: ApisixRoute
200+
metadata:
201+
name: httpbin-route
202+
spec:
203+
http:
204+
- name: rule1
205+
match:
206+
hosts:
207+
- httpbin.org
208+
paths:
209+
- /ip
210+
exprs:
211+
- subject:
212+
scope: Query
213+
name: id
214+
op: GreaterThanEqual
215+
value: "13"
216+
backends:
217+
- serviceName: %s
218+
servicePort: %d
219+
`, backendSvc, backendPorts[0])
220+
221+
assert.Nil(ginkgo.GinkgoT(), s.CreateVersionedApisixResource(ar))
222+
223+
time.Sleep(6 * time.Second)
224+
err := s.EnsureNumApisixRoutesCreated(1)
225+
assert.Nil(ginkgo.GinkgoT(), err, "Checking number of routes")
226+
err = s.EnsureNumApisixUpstreamsCreated(1)
227+
assert.Nil(ginkgo.GinkgoT(), err, "Checking number of upstreams")
228+
229+
_ = s.NewAPISIXClient().GET("/ip").
230+
WithHeader("Host", "httpbin.org").
231+
WithQuery("id", 100).
232+
Expect().
233+
Status(http.StatusOK)
234+
235+
_ = s.NewAPISIXClient().GET("/ip").
236+
WithHeader("Host", "httpbin.org").
237+
WithQuery("id", 13).
238+
Expect().
239+
Status(http.StatusOK)
240+
241+
msg := s.NewAPISIXClient().GET("/ip").
242+
WithHeader("Host", "httpbin.org").
243+
WithQuery("id", 10).
244+
Expect().
245+
Status(http.StatusNotFound).
246+
Body().
247+
Raw()
248+
assert.Contains(ginkgo.GinkgoT(), msg, "404 Route Not Found")
249+
250+
msg = s.NewAPISIXClient().GET("/ip").
251+
WithHeader("Host", "httpbin.org").
252+
Expect().
253+
Status(http.StatusNotFound).
254+
Body().
255+
Raw()
256+
assert.Contains(ginkgo.GinkgoT(), msg, "404 Route Not Found")
257+
})
258+
190259
ginkgo.It("operator is less_than", func() {
191260
backendSvc, backendPorts := s.DefaultHTTPBackend()
192261

@@ -246,6 +315,75 @@ spec:
246315
assert.Contains(ginkgo.GinkgoT(), msg, "404 Route Not Found")
247316
})
248317

318+
ginkgo.It("operator is LessThanEqual", func() {
319+
if s.ApisixResourceVersion() == scaffold.ApisixResourceVersion().V2beta3 {
320+
ginkgo.Skip("Not support ApisixRoute v2beta3")
321+
}
322+
323+
backendSvc, backendPorts := s.DefaultHTTPBackend()
324+
325+
ar := fmt.Sprintf(`
326+
apiVersion: apisix.apache.org/v2
327+
kind: ApisixRoute
328+
metadata:
329+
name: httpbin-route
330+
spec:
331+
http:
332+
- name: rule1
333+
match:
334+
hosts:
335+
- httpbin.org
336+
paths:
337+
- /ip
338+
exprs:
339+
- subject:
340+
scope: Query
341+
name: ID
342+
op: LessThanEqual
343+
value: "13"
344+
backends:
345+
- serviceName: %s
346+
servicePort: %d
347+
`, backendSvc, backendPorts[0])
348+
349+
assert.Nil(ginkgo.GinkgoT(), s.CreateVersionedApisixResource(ar))
350+
351+
time.Sleep(6 * time.Second)
352+
err := s.EnsureNumApisixRoutesCreated(1)
353+
assert.Nil(ginkgo.GinkgoT(), err, "Checking number of routes")
354+
err = s.EnsureNumApisixUpstreamsCreated(1)
355+
assert.Nil(ginkgo.GinkgoT(), err, "Checking number of upstreams")
356+
357+
_ = s.NewAPISIXClient().GET("/ip").
358+
WithHeader("Host", "httpbin.org").
359+
WithQuery("ID", 12).
360+
Expect().
361+
Status(http.StatusOK)
362+
363+
_ = s.NewAPISIXClient().GET("/ip").
364+
WithHeader("Host", "httpbin.org").
365+
WithQuery("ID", 13).
366+
Expect().
367+
Status(http.StatusOK)
368+
369+
msg := s.NewAPISIXClient().GET("/ip").
370+
WithHeader("Host", "httpbin.org").
371+
WithQuery("ID", 14).
372+
Expect().
373+
Status(http.StatusNotFound).
374+
Body().
375+
Raw()
376+
assert.Contains(ginkgo.GinkgoT(), msg, "404 Route Not Found")
377+
378+
msg = s.NewAPISIXClient().GET("/ip").
379+
WithHeader("Host", "httpbin.org").
380+
Expect().
381+
Status(http.StatusNotFound).
382+
Body().
383+
Raw()
384+
assert.Contains(ginkgo.GinkgoT(), msg, "404 Route Not Found")
385+
})
386+
249387
ginkgo.It("operator is in", func() {
250388
backendSvc, backendPorts := s.DefaultHTTPBackend()
251389

@@ -683,3 +821,164 @@ spec:
683821
suites(scaffold.NewDefaultV2Scaffold)
684822
})
685823
})
824+
825+
var _ = ginkgo.Describe("suite-features: route match exprs with variable", func() {
826+
s := scaffold.NewDefaultScaffold()
827+
ginkgo.It("exprs with request_method variable", func() {
828+
backendSvc, backendPorts := s.DefaultHTTPBackend()
829+
830+
ar := fmt.Sprintf(`
831+
apiVersion: apisix.apache.org/v2
832+
kind: ApisixRoute
833+
metadata:
834+
name: httpbin-route
835+
spec:
836+
http:
837+
- name: rule1
838+
match:
839+
hosts:
840+
- httpbin.org
841+
paths:
842+
- /get
843+
- /post
844+
- /put
845+
exprs:
846+
- subject:
847+
scope: Variable
848+
name: request_method
849+
op: In
850+
set:
851+
- GET
852+
- POST
853+
backends:
854+
- serviceName: %s
855+
servicePort: %d
856+
`, backendSvc, backendPorts[0])
857+
858+
assert.Nil(ginkgo.GinkgoT(), s.CreateVersionedApisixResource(ar), "creating route")
859+
time.Sleep(6 * time.Second)
860+
assert.Nil(ginkgo.GinkgoT(), s.EnsureNumApisixRoutesCreated(1), "Checking number of routes")
861+
862+
_ = s.NewAPISIXClient().GET("/get").
863+
WithHeader("Host", "httpbin.org").
864+
Expect().
865+
Status(http.StatusOK)
866+
867+
_ = s.NewAPISIXClient().POST("/post").
868+
WithHeader("Host", "httpbin.org").
869+
Expect().
870+
Status(http.StatusOK)
871+
872+
_ = s.NewAPISIXClient().PUT("/put").
873+
WithHeader("Host", "httpbin.org").
874+
Expect().
875+
Status(http.StatusNotFound)
876+
})
877+
878+
ginkgo.It("exprs with host variable", func() {
879+
backendSvc, backendPorts := s.DefaultHTTPBackend()
880+
881+
ar := fmt.Sprintf(`
882+
apiVersion: apisix.apache.org/v2
883+
kind: ApisixRoute
884+
metadata:
885+
name: httpbin-route
886+
spec:
887+
http:
888+
- name: rule1
889+
match:
890+
paths:
891+
- /ip
892+
exprs:
893+
- subject:
894+
scope: Variable
895+
name: host
896+
op: In
897+
set:
898+
- httpbin.net
899+
- httpbin.org
900+
- httpbin.com
901+
backends:
902+
- serviceName: %s
903+
servicePort: %d
904+
`, backendSvc, backendPorts[0])
905+
906+
assert.Nil(ginkgo.GinkgoT(), s.CreateVersionedApisixResource(ar), "creating route")
907+
time.Sleep(6 * time.Second)
908+
assert.Nil(ginkgo.GinkgoT(), s.EnsureNumApisixRoutesCreated(1), "Checking number of routes")
909+
910+
_ = s.NewAPISIXClient().GET("/ip").
911+
WithHeader("Host", "httpbin.net").
912+
Expect().
913+
Status(http.StatusOK)
914+
915+
_ = s.NewAPISIXClient().GET("/ip").
916+
WithHeader("Host", "httpbin.org").
917+
Expect().
918+
Status(http.StatusOK)
919+
920+
_ = s.NewAPISIXClient().GET("/ip").
921+
WithHeader("Host", "httpbin.com").
922+
Expect().
923+
Status(http.StatusOK)
924+
})
925+
926+
ginkgo.It("exprs request_method and host variable", func() {
927+
backendSvc, backendPorts := s.DefaultHTTPBackend()
928+
929+
ar := fmt.Sprintf(`
930+
apiVersion: apisix.apache.org/v2
931+
kind: ApisixRoute
932+
metadata:
933+
name: httpbin-route
934+
spec:
935+
http:
936+
- name: rule1
937+
match:
938+
paths:
939+
- /*
940+
exprs:
941+
- subject:
942+
scope: Variable
943+
name: request_method
944+
op: In
945+
set:
946+
- GET
947+
- PUT
948+
- subject:
949+
scope: Variable
950+
name: host
951+
op: In
952+
set:
953+
- httpbin.org
954+
- httpbin.com
955+
backends:
956+
- serviceName: %s
957+
servicePort: %d
958+
`, backendSvc, backendPorts[0])
959+
960+
assert.Nil(ginkgo.GinkgoT(), s.CreateVersionedApisixResource(ar), "creating route")
961+
time.Sleep(6 * time.Second)
962+
assert.Nil(ginkgo.GinkgoT(), s.EnsureNumApisixRoutesCreated(1), "Checking number of routes")
963+
964+
_ = s.NewAPISIXClient().GET("/get").
965+
WithHeader("Host", "httpbin.net").
966+
Expect().
967+
Status(http.StatusNotFound)
968+
969+
_ = s.NewAPISIXClient().GET("/get").
970+
WithHeader("Host", "httpbin.org").
971+
Expect().
972+
Status(http.StatusOK)
973+
974+
_ = s.NewAPISIXClient().POST("/post").
975+
WithHeader("Host", "httpbin.org").
976+
Expect().
977+
Status(http.StatusNotFound)
978+
979+
_ = s.NewAPISIXClient().PUT("/put").
980+
WithHeader("Host", "httpbin.com").
981+
Expect().
982+
Status(http.StatusOK)
983+
})
984+
})

0 commit comments

Comments
 (0)