Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions internal/provider/adc/translator/apisixroute.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,10 +198,10 @@ func (t *Translator) buildUpstream(tctx *provider.TranslateContext, service *adc
var (
upstreams = make([]*adc.Upstream, 0)
weightedUpstreams = make([]adc.TrafficSplitConfigRuleWeightedUpstream, 0)
backendErr error
)

for _, backend := range rule.Backends {
var backendErr error
upstream := adc.NewDefaultUpstream()
// try to get the apisixupstream with the same name as the backend service to be upstream config.
// err is ignored because it does not care about the externalNodes of the apisixupstream.
Expand All @@ -223,7 +223,9 @@ func (t *Translator) buildUpstream(tctx *provider.TranslateContext, service *adc
continue
}
}

if backend.Weight != nil {
upstream.Labels["meta_weight"] = strconv.FormatInt(int64(*backend.Weight), 10)
}
upstreams = append(upstreams, upstream)
}

Expand All @@ -250,7 +252,7 @@ func (t *Translator) buildUpstream(tctx *provider.TranslateContext, service *adc
}

// no valid upstream
if backendErr != nil || len(upstreams) == 0 || len(upstreams[0].Nodes) == 0 {
if len(upstreams) == 0 || len(upstreams[0].Nodes) == 0 {
return
}

Expand Down
131 changes: 131 additions & 0 deletions test/e2e/crds/v2/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"context"
"fmt"
"io"
"math"
"net"
"net/http"
"time"
Expand Down Expand Up @@ -651,6 +652,136 @@ spec:
})
})

Context("Test ApisixRoute Traffic Split", func() {
It("2:1 traffic split test", func() {
const apisixRouteSpec = `
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
name: default
spec:
ingressClassName: apisix
http:
- name: rule1
match:
hosts:
- httpbin.org
paths:
- /get
backends:
- serviceName: httpbin-service-e2e-test
servicePort: 80
weight: 10
- serviceName: %s
servicePort: 9180
weight: 5
`
By("apply ApisixRoute with traffic split")
applier.MustApplyAPIv2(types.NamespacedName{Namespace: s.Namespace(), Name: "default"}, new(apiv2.ApisixRoute),
fmt.Sprintf(apisixRouteSpec, s.Deployer.GetAdminServiceName()))
verifyRequest := func() int {
return s.NewAPISIXClient().GET("/get").WithHost("httpbin.org").Expect().Raw().StatusCode
}
By("send requests to verify traffic split")
var (
successCount int
failCount int
)

time.Sleep(10 * time.Second)
for range 90 {
code := verifyRequest()
if code == http.StatusOK {
successCount++
} else {
failCount++
}
}

By("verify traffic distribution ratio")
ratio := float64(successCount) / float64(failCount)
expectedRatio := 10.0 / 5.0 // 2:1 ratio
deviation := math.Abs(ratio - expectedRatio)
Expect(deviation).Should(BeNumerically("<", 0.5),
"traffic distribution deviation too large (got %.2f, expected %.2f)", ratio, expectedRatio)
})

It("zero-weight test", func() {
const apisixRouteSpec = `
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
name: default
spec:
ingressClassName: apisix
http:
- name: rule1
match:
hosts:
- httpbin.org
paths:
- /get
backends:
- serviceName: httpbin-service-e2e-test
servicePort: 80
weight: 10
- serviceName: %s
servicePort: 9180
weight: 0
`
By("apply ApisixRoute with zero-weight backend")
applier.MustApplyAPIv2(types.NamespacedName{Namespace: s.Namespace(), Name: "default"}, new(apiv2.ApisixRoute),
fmt.Sprintf(apisixRouteSpec, s.Deployer.GetAdminServiceName()))
verifyRequest := func() int {
return s.NewAPISIXClient().GET("/get").WithHost("httpbin.org").Expect().Raw().StatusCode
}

By("wait for route to be ready")
time.Sleep(8 * time.Second)
By("send requests to verify zero-weight behavior")
for range 30 {
code := verifyRequest()
Expect(code).Should(Equal(200))
}
})
It("valid backend is set even if other backend is invalid", func() {
const apisixRouteSpec = `
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
name: default
spec:
ingressClassName: apisix
http:
- name: rule1
match:
hosts:
- httpbin.org
paths:
- /get
backends:
- serviceName: httpbin-service-e2e-test
servicePort: 80
weight: 10
- serviceName: invalid-service
servicePort: 9180
weight: 5
`
By("apply ApisixRoute with traffic split")
applier.MustApplyAPIv2(types.NamespacedName{Namespace: s.Namespace(), Name: "default"}, new(apiv2.ApisixRoute), apisixRouteSpec)
verifyRequest := func() int {
return s.NewAPISIXClient().GET("/get").WithHost("httpbin.org").Expect().Raw().StatusCode
}

By("wait for route to be ready")
time.Sleep(8 * time.Second)
By("send requests to verify all requests routed to valid upstream")
for range 30 {
code := verifyRequest()
Expect(code).Should(Equal(200))
}
})
})
Context("Test ApisixRoute sync during startup", func() {
const route = `
apiVersion: apisix.apache.org/v2
Expand Down
3 changes: 3 additions & 0 deletions test/e2e/scaffold/apisix_deployer.go
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,9 @@ func (s *APISIXDeployer) GetAdminEndpoint(svc ...*corev1.Service) string {
return fmt.Sprintf("http://%s.%s:9180", svc[0].Name, svc[0].Namespace)
}

func (s *APISIXDeployer) GetAdminServiceName() string {
return s.dataplaneService.Name
}
func (s *APISIXDeployer) DefaultDataplaneResource() DataplaneResource {
return newADCDataplaneResource(
framework.ProviderType,
Expand Down
1 change: 1 addition & 0 deletions test/e2e/scaffold/deployer.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type Deployer interface {
CreateAdditionalGateway(namePrefix string) (string, *corev1.Service, error)
CleanupAdditionalGateway(identifier string) error
GetAdminEndpoint(...*corev1.Service) string
GetAdminServiceName() string
DefaultDataplaneResource() DataplaneResource
}

Expand Down
Loading