-
Notifications
You must be signed in to change notification settings - Fork 366
fix: handle httproute multi backend refs #2540
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -33,6 +33,7 @@ import ( | |
|
|
||
| adctypes "github.com/apache/apisix-ingress-controller/api/adc" | ||
| "github.com/apache/apisix-ingress-controller/api/v1alpha1" | ||
| apiv2 "github.com/apache/apisix-ingress-controller/api/v2" | ||
| "github.com/apache/apisix-ingress-controller/internal/controller/label" | ||
| "github.com/apache/apisix-ingress-controller/internal/id" | ||
| "github.com/apache/apisix-ingress-controller/internal/provider" | ||
|
|
@@ -466,32 +467,89 @@ func (t *Translator) TranslateHTTPRoute(tctx *provider.TranslateContext, httpRou | |
| labels := label.GenLabel(httpRoute) | ||
|
|
||
| for ruleIndex, rule := range rules { | ||
| upstream := adctypes.NewDefaultUpstream() | ||
| var backendErr error | ||
| service := adctypes.NewDefaultService() | ||
| service.Labels = labels | ||
|
|
||
| service.Name = adctypes.ComposeServiceNameWithRule(httpRoute.Namespace, httpRoute.Name, fmt.Sprintf("%d", ruleIndex)) | ||
| service.ID = id.GenID(service.Name) | ||
| service.Hosts = hosts | ||
|
|
||
| var ( | ||
| upstreams = make([]*adctypes.Upstream, 0) | ||
| weightedUpstreams = make([]adctypes.TrafficSplitConfigRuleWeightedUpstream, 0) | ||
| backendErr error | ||
| ) | ||
|
|
||
| for _, backend := range rule.BackendRefs { | ||
| if backend.Namespace == nil { | ||
| namespace := gatewayv1.Namespace(httpRoute.Namespace) | ||
| backend.Namespace = &namespace | ||
| } | ||
| upstream := adctypes.NewDefaultUpstream() | ||
| upNodes, err := t.translateBackendRef(tctx, backend.BackendRef, DefaultEndpointFilter) | ||
| if err != nil { | ||
| backendErr = err | ||
| continue | ||
| } | ||
| if len(upNodes) == 0 { | ||
| continue | ||
| } | ||
|
|
||
| t.AttachBackendTrafficPolicyToUpstream(backend.BackendRef, tctx.BackendTrafficPolicies, upstream) | ||
| upstream.Nodes = append(upstream.Nodes, upNodes...) | ||
| upstream.Nodes = upNodes | ||
| upstreams = append(upstreams, upstream) | ||
| } | ||
|
|
||
| // todo: support multiple backends | ||
| service := adctypes.NewDefaultService() | ||
| service.Labels = labels | ||
| // Handle multiple backends with traffic-split plugin | ||
| if len(upstreams) == 0 { | ||
| // Create a default upstream if no valid backends | ||
| upstream := adctypes.NewDefaultUpstream() | ||
| service.Upstream = upstream | ||
| } else if len(upstreams) == 1 { | ||
| // Single backend - use directly as service upstream | ||
| service.Upstream = upstreams[0] | ||
| } else { | ||
| // Multiple backends - use traffic-split plugin | ||
| service.Upstream = upstreams[0] | ||
| upstreams = upstreams[1:] | ||
|
|
||
| // Set weight in traffic-split for the default upstream | ||
| weight := apiv2.DefaultWeight | ||
| if rule.BackendRefs[0].Weight != nil { | ||
| weight = int(*rule.BackendRefs[0].Weight) | ||
| } | ||
| weightedUpstreams = append(weightedUpstreams, adctypes.TrafficSplitConfigRuleWeightedUpstream{ | ||
| Weight: weight, | ||
| }) | ||
|
|
||
| service.Name = adctypes.ComposeServiceNameWithRule(httpRoute.Namespace, httpRoute.Name, fmt.Sprintf("%d", ruleIndex)) | ||
| service.ID = id.GenID(service.Name) | ||
| service.Hosts = hosts | ||
| service.Upstream = upstream | ||
| // Set other upstreams in traffic-split | ||
| for i, upstream := range upstreams { | ||
| weight := apiv2.DefaultWeight | ||
| // get weight from the backend ref start from the second backend | ||
ronething marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| if i+1 < len(rule.BackendRefs) && rule.BackendRefs[i+1].Weight != nil { | ||
| weight = int(*rule.BackendRefs[i+1].Weight) | ||
| } | ||
|
||
| weightedUpstreams = append(weightedUpstreams, adctypes.TrafficSplitConfigRuleWeightedUpstream{ | ||
| Upstream: upstream, | ||
| Weight: weight, | ||
| }) | ||
| } | ||
|
|
||
| if len(weightedUpstreams) > 0 { | ||
| if service.Plugins == nil { | ||
| service.Plugins = make(map[string]any) | ||
| } | ||
| service.Plugins["traffic-split"] = &adctypes.TrafficSplitConfig{ | ||
| Rules: []adctypes.TrafficSplitConfigRule{ | ||
| { | ||
| WeightedUpstreams: weightedUpstreams, | ||
| }, | ||
| }, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if backendErr != nil && len(upstream.Nodes) == 0 { | ||
| if backendErr != nil && (service.Upstream == nil || len(service.Upstream.Nodes) == 0) { | ||
| if service.Plugins == nil { | ||
| service.Plugins = make(map[string]any) | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -37,6 +37,7 @@ var ( | |
|
|
||
| type NginxOptions struct { | ||
| Namespace string | ||
| Replicas *int32 | ||
| } | ||
|
|
||
| func init() { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem is the same as the previous one: it's best to avoid using inline upstreams. Otherwise, any change to the endpoints will trigger a complete rebuild of the routing tree. Essentially, that involves modifying plugins on the service, which is equivalent to modifying the service itself.
Instead, ADC provides full capabilities to define separate upstream resources and assign IDs. You can then reference those IDs in traffic-split. This functionality is available out of the box for every ADC backend.
Changes to each individual upstream are independent and do not cause changes to the service or route. This avoids unnecessary rebuilds of the routing tree and meaningless changes to the conf version.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, we are aware this issue currently exists. It's not limited to HTTPRoute, similar problems also exist in the current translate logic of APISIXRoute. After discussing with @AlinsRan this morning, we've decided to address this uniformly in the next pull request. This pull request is solely intended to resolve the previous issue where multiple Upstreams were not correctly utilizing traffic-split plugin.
What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great, as long as we can finish it before the next release. 🫡