Skip to content

Commit ec8624b

Browse files
ronethingCopilot
andauthored
fix: handle httproute multi backend refs (#2540)
Signed-off-by: Ashing Zheng <[email protected]> Co-authored-by: Copilot <[email protected]>
1 parent c831c16 commit ec8624b

File tree

5 files changed

+75
-14
lines changed

5 files changed

+75
-14
lines changed

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ require (
1010
github.com/gavv/httpexpect/v2 v2.16.0
1111
github.com/go-logr/logr v1.4.2
1212
github.com/go-logr/zapr v1.3.0
13+
github.com/google/go-cmp v0.6.0
1314
github.com/google/uuid v1.6.0
15+
github.com/gorilla/websocket v1.5.1
1416
github.com/gruntwork-io/terratest v0.50.0
1517
github.com/hashicorp/go-memdb v1.3.4
1618
github.com/incubator4/go-resty-expr v0.1.1
@@ -108,11 +110,9 @@ require (
108110
github.com/golang/protobuf v1.5.4 // indirect
109111
github.com/google/cel-go v0.20.1 // indirect
110112
github.com/google/gnostic-models v0.6.8 // indirect
111-
github.com/google/go-cmp v0.6.0 // indirect
112113
github.com/google/go-querystring v1.1.0 // indirect
113114
github.com/google/gofuzz v1.2.0 // indirect
114115
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 // indirect
115-
github.com/gorilla/websocket v1.5.1 // indirect
116116
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
117117
github.com/gruntwork-io/go-commons v0.8.0 // indirect
118118
github.com/hashicorp/errwrap v1.1.0 // indirect

internal/adc/translator/httproute.go

Lines changed: 69 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import (
3333

3434
adctypes "github.com/apache/apisix-ingress-controller/api/adc"
3535
"github.com/apache/apisix-ingress-controller/api/v1alpha1"
36+
apiv2 "github.com/apache/apisix-ingress-controller/api/v2"
3637
"github.com/apache/apisix-ingress-controller/internal/controller/label"
3738
"github.com/apache/apisix-ingress-controller/internal/id"
3839
"github.com/apache/apisix-ingress-controller/internal/provider"
@@ -466,32 +467,89 @@ func (t *Translator) TranslateHTTPRoute(tctx *provider.TranslateContext, httpRou
466467
labels := label.GenLabel(httpRoute)
467468

468469
for ruleIndex, rule := range rules {
469-
upstream := adctypes.NewDefaultUpstream()
470-
var backendErr error
470+
service := adctypes.NewDefaultService()
471+
service.Labels = labels
472+
473+
service.Name = adctypes.ComposeServiceNameWithRule(httpRoute.Namespace, httpRoute.Name, fmt.Sprintf("%d", ruleIndex))
474+
service.ID = id.GenID(service.Name)
475+
service.Hosts = hosts
476+
477+
var (
478+
upstreams = make([]*adctypes.Upstream, 0)
479+
weightedUpstreams = make([]adctypes.TrafficSplitConfigRuleWeightedUpstream, 0)
480+
backendErr error
481+
)
482+
471483
for _, backend := range rule.BackendRefs {
472484
if backend.Namespace == nil {
473485
namespace := gatewayv1.Namespace(httpRoute.Namespace)
474486
backend.Namespace = &namespace
475487
}
488+
upstream := adctypes.NewDefaultUpstream()
476489
upNodes, err := t.translateBackendRef(tctx, backend.BackendRef, DefaultEndpointFilter)
477490
if err != nil {
478491
backendErr = err
479492
continue
480493
}
494+
if len(upNodes) == 0 {
495+
continue
496+
}
497+
481498
t.AttachBackendTrafficPolicyToUpstream(backend.BackendRef, tctx.BackendTrafficPolicies, upstream)
482-
upstream.Nodes = append(upstream.Nodes, upNodes...)
499+
upstream.Nodes = upNodes
500+
upstreams = append(upstreams, upstream)
483501
}
484502

485-
// todo: support multiple backends
486-
service := adctypes.NewDefaultService()
487-
service.Labels = labels
503+
// Handle multiple backends with traffic-split plugin
504+
if len(upstreams) == 0 {
505+
// Create a default upstream if no valid backends
506+
upstream := adctypes.NewDefaultUpstream()
507+
service.Upstream = upstream
508+
} else if len(upstreams) == 1 {
509+
// Single backend - use directly as service upstream
510+
service.Upstream = upstreams[0]
511+
} else {
512+
// Multiple backends - use traffic-split plugin
513+
service.Upstream = upstreams[0]
514+
upstreams = upstreams[1:]
515+
516+
// Set weight in traffic-split for the default upstream
517+
weight := apiv2.DefaultWeight
518+
if rule.BackendRefs[0].Weight != nil {
519+
weight = int(*rule.BackendRefs[0].Weight)
520+
}
521+
weightedUpstreams = append(weightedUpstreams, adctypes.TrafficSplitConfigRuleWeightedUpstream{
522+
Weight: weight,
523+
})
488524

489-
service.Name = adctypes.ComposeServiceNameWithRule(httpRoute.Namespace, httpRoute.Name, fmt.Sprintf("%d", ruleIndex))
490-
service.ID = id.GenID(service.Name)
491-
service.Hosts = hosts
492-
service.Upstream = upstream
525+
// Set other upstreams in traffic-split
526+
for i, upstream := range upstreams {
527+
weight := apiv2.DefaultWeight
528+
// get weight from the backend refs starting from the second backend
529+
if i+1 < len(rule.BackendRefs) && rule.BackendRefs[i+1].Weight != nil {
530+
weight = int(*rule.BackendRefs[i+1].Weight)
531+
}
532+
weightedUpstreams = append(weightedUpstreams, adctypes.TrafficSplitConfigRuleWeightedUpstream{
533+
Upstream: upstream,
534+
Weight: weight,
535+
})
536+
}
537+
538+
if len(weightedUpstreams) > 0 {
539+
if service.Plugins == nil {
540+
service.Plugins = make(map[string]any)
541+
}
542+
service.Plugins["traffic-split"] = &adctypes.TrafficSplitConfig{
543+
Rules: []adctypes.TrafficSplitConfigRule{
544+
{
545+
WeightedUpstreams: weightedUpstreams,
546+
},
547+
},
548+
}
549+
}
550+
}
493551

494-
if backendErr != nil && len(upstream.Nodes) == 0 {
552+
if backendErr != nil && (service.Upstream == nil || len(service.Upstream.Nodes) == 0) {
495553
if service.Plugins == nil {
496554
service.Plugins = make(map[string]any)
497555
}

test/e2e/framework/manifests/nginx.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ kind: Deployment
4444
metadata:
4545
name: nginx
4646
spec:
47-
replicas: 1
47+
replicas: {{ .Replicas | default 1 }}
4848
selector:
4949
matchLabels:
5050
app: nginx

test/e2e/framework/nginx.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ var (
3737

3838
type NginxOptions struct {
3939
Namespace string
40+
Replicas *int32
4041
}
4142

4243
func init() {

test/e2e/gatewayapi/httproute.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
corev1 "k8s.io/api/core/v1"
3131
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3232
"k8s.io/apimachinery/pkg/types"
33+
"k8s.io/utils/ptr"
3334
"sigs.k8s.io/gateway-api/apis/v1alpha2"
3435

3536
"github.com/apache/apisix-ingress-controller/api/v1alpha1"
@@ -1916,6 +1917,7 @@ spec:
19161917
beforeEachHTTP()
19171918
s.DeployNginx(framework.NginxOptions{
19181919
Namespace: s.Namespace(),
1920+
Replicas: ptr.To(int32(2)),
19191921
})
19201922
})
19211923
It("HTTPRoute Canary", func() {

0 commit comments

Comments
 (0)