@@ -21,6 +21,7 @@ import (
2121 "context"
2222 "fmt"
2323 "io"
24+ "math"
2425 "net"
2526 "net/http"
2627 "time"
@@ -651,6 +652,154 @@ spec:
651652 })
652653 })
653654
655+ Context ("Test ApisixRoute Traffic Split" , func () {
656+ It ("2:1 traffic split test" , func () {
657+ const apisixRouteSpec = `
658+ apiVersion: apisix.apache.org/v2
659+ kind: ApisixRoute
660+ metadata:
661+ name: default
662+ spec:
663+ ingressClassName: apisix
664+ http:
665+ - name: rule1
666+ match:
667+ hosts:
668+ - httpbin.org
669+ paths:
670+ - /get
671+ backends:
672+ - serviceName: httpbin-service-e2e-test
673+ servicePort: 80
674+ weight: 10
675+ - serviceName: %s
676+ servicePort: 9180
677+ weight: 5
678+ `
679+ By ("apply ApisixRoute with traffic split" )
680+ applier .MustApplyAPIv2 (types.NamespacedName {Namespace : s .Namespace (), Name : "default" }, new (apiv2.ApisixRoute ),
681+ fmt .Sprintf (apisixRouteSpec , s .Deployer .GetAdminServiceName ()))
682+ verifyRequest := func () int {
683+ return s .NewAPISIXClient ().GET ("/get" ).WithHost ("httpbin.org" ).Expect ().Raw ().StatusCode
684+ }
685+ By ("send requests to verify traffic split" )
686+ var (
687+ successCount int
688+ failCount int
689+ )
690+
691+ s .RequestAssert (& scaffold.RequestAssert {
692+ Method : "GET" ,
693+ Path : "/get" ,
694+ Host : "httpbin.org" ,
695+ Check : scaffold .WithExpectedStatus (http .StatusOK ),
696+ Timeout : 10 * time .Second ,
697+ })
698+ for range 90 {
699+ code := verifyRequest ()
700+ if code == http .StatusOK {
701+ successCount ++
702+ } else {
703+ failCount ++
704+ }
705+ }
706+
707+ By ("verify traffic distribution ratio" )
708+ ratio := float64 (successCount ) / float64 (failCount )
709+ expectedRatio := 10.0 / 5.0 // 2:1 ratio
710+ deviation := math .Abs (ratio - expectedRatio )
711+ Expect (deviation ).Should (BeNumerically ("<" , 0.5 ),
712+ "traffic distribution deviation too large (got %.2f, expected %.2f)" , ratio , expectedRatio )
713+ })
714+
715+ It ("zero-weight test" , func () {
716+ const apisixRouteSpec = `
717+ apiVersion: apisix.apache.org/v2
718+ kind: ApisixRoute
719+ metadata:
720+ name: default
721+ spec:
722+ ingressClassName: apisix
723+ http:
724+ - name: rule1
725+ match:
726+ hosts:
727+ - httpbin.org
728+ paths:
729+ - /get
730+ backends:
731+ - serviceName: httpbin-service-e2e-test
732+ servicePort: 80
733+ weight: 10
734+ - serviceName: %s
735+ servicePort: 9180
736+ weight: 0
737+ `
738+ By ("apply ApisixRoute with zero-weight backend" )
739+ applier .MustApplyAPIv2 (types.NamespacedName {Namespace : s .Namespace (), Name : "default" }, new (apiv2.ApisixRoute ),
740+ fmt .Sprintf (apisixRouteSpec , s .Deployer .GetAdminServiceName ()))
741+ verifyRequest := func () int {
742+ return s .NewAPISIXClient ().GET ("/get" ).WithHost ("httpbin.org" ).Expect ().Raw ().StatusCode
743+ }
744+
745+ By ("wait for route to be ready" )
746+ s .RequestAssert (& scaffold.RequestAssert {
747+ Method : "GET" ,
748+ Path : "/get" ,
749+ Host : "httpbin.org" ,
750+ Check : scaffold .WithExpectedStatus (http .StatusOK ),
751+ Timeout : 10 * time .Second ,
752+ })
753+ By ("send requests to verify zero-weight behavior" )
754+ for range 30 {
755+ code := verifyRequest ()
756+ Expect (code ).Should (Equal (200 ))
757+ }
758+ })
759+ It ("valid backend is set even if other backend is invalid" , func () {
760+ const apisixRouteSpec = `
761+ apiVersion: apisix.apache.org/v2
762+ kind: ApisixRoute
763+ metadata:
764+ name: default
765+ spec:
766+ ingressClassName: apisix
767+ http:
768+ - name: rule1
769+ match:
770+ hosts:
771+ - httpbin.org
772+ paths:
773+ - /get
774+ backends:
775+ - serviceName: httpbin-service-e2e-test
776+ servicePort: 80
777+ weight: 10
778+ - serviceName: invalid-service
779+ servicePort: 9180
780+ weight: 5
781+ `
782+ By ("apply ApisixRoute with traffic split" )
783+ applier .MustApplyAPIv2 (types.NamespacedName {Namespace : s .Namespace (), Name : "default" }, new (apiv2.ApisixRoute ), apisixRouteSpec )
784+ verifyRequest := func () int {
785+ return s .NewAPISIXClient ().GET ("/get" ).WithHost ("httpbin.org" ).Expect ().Raw ().StatusCode
786+ }
787+
788+ By ("wait for route to be ready" )
789+ s .RequestAssert (& scaffold.RequestAssert {
790+ Method : "GET" ,
791+ Path : "/get" ,
792+ Host : "httpbin.org" ,
793+ Check : scaffold .WithExpectedStatus (http .StatusOK ),
794+ Timeout : 10 * time .Second ,
795+ })
796+ By ("send requests to verify all requests routed to valid upstream" )
797+ for range 30 {
798+ code := verifyRequest ()
799+ Expect (code ).Should (Equal (200 ))
800+ }
801+ })
802+ })
654803 Context ("Test ApisixRoute sync during startup" , func () {
655804 const route = `
656805apiVersion: apisix.apache.org/v2
0 commit comments