Skip to content

Commit b5e8e66

Browse files
authored
feat(apisixupstream): support portLevelSettings (#2582) (#284)
1 parent 75b4e6d commit b5e8e66

File tree

8 files changed

+416
-68
lines changed

8 files changed

+416
-68
lines changed

api/v2/apisixroute_types.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,15 @@ type ApisixRouteSpec struct {
3636
IngressClassName string `json:"ingressClassName,omitempty" yaml:"ingressClassName,omitempty"`
3737
// HTTP defines a list of HTTP route rules.
3838
// Each rule specifies conditions to match HTTP requests and how to forward them.
39+
//
40+
// +listType=map
41+
// +listMapKey=name
3942
HTTP []ApisixRouteHTTP `json:"http,omitempty" yaml:"http,omitempty"`
4043
// Stream defines a list of stream route rules.
4144
// Each rule specifies conditions to match TCP/UDP traffic and how to forward them.
45+
//
46+
// +listType=map
47+
// +listMapKey=name
4248
Stream []ApisixRouteStream `json:"stream,omitempty" yaml:"stream,omitempty"`
4349
}
4450

config/crd/bases/apisix.apache.org_apisixroutes.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,9 @@ spec:
347347
- name
348348
type: object
349349
type: array
350+
x-kubernetes-list-map-keys:
351+
- name
352+
x-kubernetes-list-type: map
350353
ingressClassName:
351354
description: |-
352355
IngressClassName is the name of the IngressClass this route belongs to.
@@ -460,6 +463,9 @@ spec:
460463
- protocol
461464
type: object
462465
type: array
466+
x-kubernetes-list-map-keys:
467+
- name
468+
x-kubernetes-list-type: map
463469
type: object
464470
status:
465471
description: ApisixStatus is the status report for Apisix ingress Resources

internal/adc/translator/apisixroute.go

Lines changed: 71 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -211,30 +211,12 @@ func (t *Translator) buildUpstream(tctx *provider.TranslateContext, service *adc
211211
)
212212

213213
for backendIndex, backend := range rule.Backends {
214-
var backendErr error
215-
upstream := adc.NewDefaultUpstream()
216214
// try to get the apisixupstream with the same name as the backend service to be upstream config.
217215
// err is ignored because it does not care about the externalNodes of the apisixupstream.
218-
auNN := types.NamespacedName{Namespace: ar.GetNamespace(), Name: backend.ServiceName}
219-
if au, ok := tctx.Upstreams[auNN]; ok {
220-
upstream, _ = t.translateApisixUpstream(tctx, au)
221-
}
222-
223-
if backend.ResolveGranularity == apiv2.ResolveGranularityService {
224-
upstream.Nodes, backendErr = t.translateApisixRouteBackendResolveGranularityService(tctx, utils.NamespacedName(ar), backend)
225-
if backendErr != nil {
226-
t.Log.Error(backendErr, "failed to translate ApisixRoute backend with ResolveGranularity Service")
227-
continue
228-
}
229-
} else {
230-
upstream.Nodes, backendErr = t.translateApisixRouteBackendResolveGranularityEndpoint(tctx, utils.NamespacedName(ar), backend)
231-
if backendErr != nil {
232-
t.Log.Error(backendErr, "failed to translate ApisixRoute backend with ResolveGranularity Endpoint")
233-
continue
234-
}
235-
}
236-
if backend.Weight != nil {
237-
upstream.Labels["meta_weight"] = strconv.FormatInt(int64(*backend.Weight), 10)
216+
upstream, err := t.translateApisixRouteHTTPBackend(tctx, ar, backend)
217+
if err != nil {
218+
t.Log.Error(err, "failed to translate ApisixRoute backend", "backend", backend)
219+
continue
238220
}
239221

240222
upstreamName := adc.ComposeUpstreamName(ar.Namespace, ar.Name, fmt.Sprintf("%d", ruleIndex), fmt.Sprintf("%d", backendIndex))
@@ -352,6 +334,46 @@ func getPortFromService(svc *v1.Service, backendSvcPort intstr.IntOrString) (int
352334
return port, nil
353335
}
354336

337+
func (t *Translator) translateApisixRouteHTTPBackend(tctx *provider.TranslateContext, ar *apiv2.ApisixRoute, backend apiv2.ApisixRouteHTTPBackend) (*adc.Upstream, error) {
338+
auNN := types.NamespacedName{
339+
Namespace: ar.Namespace,
340+
Name: backend.ServiceName,
341+
}
342+
upstream := adc.NewDefaultUpstream()
343+
if au, ok := tctx.Upstreams[auNN]; ok {
344+
svc := tctx.Services[auNN]
345+
if svc == nil {
346+
return nil, errors.Errorf("service not found, ApisixRoute: %s, Service: %s", utils.NamespacedName(ar).String(), auNN)
347+
}
348+
port, err := getPortFromService(svc, backend.ServicePort)
349+
if err != nil {
350+
return nil, err
351+
}
352+
u, err := t.translateApisixUpstreamForPort(tctx, au, ptr.To(port))
353+
if err != nil {
354+
return nil, err
355+
}
356+
upstream = u
357+
}
358+
var (
359+
err error
360+
nodes adc.UpstreamNodes
361+
)
362+
if backend.ResolveGranularity == apiv2.ResolveGranularityService {
363+
nodes, err = t.translateApisixRouteBackendResolveGranularityService(tctx, auNN, backend)
364+
} else {
365+
nodes, err = t.translateApisixRouteBackendResolveGranularityEndpoint(tctx, auNN, backend)
366+
}
367+
if err != nil {
368+
return nil, err
369+
}
370+
upstream.Nodes = nodes
371+
if backend.Weight != nil {
372+
upstream.Labels["meta_weight"] = strconv.FormatInt(int64(*backend.Weight), 10)
373+
}
374+
return upstream, nil
375+
}
376+
355377
func (t *Translator) translateApisixRouteBackendResolveGranularityService(tctx *provider.TranslateContext, arNN types.NamespacedName, backend apiv2.ApisixRouteHTTPBackend) (adc.UpstreamNodes, error) {
356378
serviceNN := types.NamespacedName{
357379
Namespace: arNN.Namespace,
@@ -435,19 +457,39 @@ func (t *Translator) translateStreamRule(tctx *provider.TranslateContext, ar *ap
435457
svc.ID = id.GenID(svc.Name)
436458
svc.StreamRoutes = append(svc.StreamRoutes, sr)
437459

438-
auNN := types.NamespacedName{Namespace: ar.GetNamespace(), Name: part.Backend.ServiceName}
439-
upstream := adc.NewDefaultUpstream()
440-
if au, ok := tctx.Upstreams[auNN]; ok {
441-
upstream, _ = t.translateApisixUpstream(tctx, au)
442-
}
443-
nodes, err := t.translateApisixRouteStreamBackendResolveGranularity(tctx, utils.NamespacedName(ar), part.Backend)
460+
upstream, err := t.translateApisixRouteStreamBackend(tctx, ar, part.Backend)
444461
if err != nil {
445462
return nil, err
446463
}
447-
upstream.Nodes = nodes
448464
upstream.ID = ""
449465
upstream.Name = ""
450466

451467
svc.Upstream = upstream
452468
return svc, nil
453469
}
470+
471+
func (t *Translator) translateApisixRouteStreamBackend(tctx *provider.TranslateContext, ar *apiv2.ApisixRoute, backend apiv2.ApisixRouteStreamBackend) (*adc.Upstream, error) {
472+
auNN := types.NamespacedName{Namespace: ar.GetNamespace(), Name: backend.ServiceName}
473+
upstream := adc.NewDefaultUpstream()
474+
if au, ok := tctx.Upstreams[auNN]; ok {
475+
service := tctx.Services[auNN]
476+
if service == nil {
477+
return nil, errors.Errorf("service not found, ApisixRoute: %s, Service: %s", utils.NamespacedName(ar), auNN)
478+
}
479+
port, err := getPortFromService(service, backend.ServicePort)
480+
if err != nil {
481+
return nil, err
482+
}
483+
u, err := t.translateApisixUpstreamForPort(tctx, au, ptr.To(port))
484+
if err != nil {
485+
return nil, err
486+
}
487+
upstream = u
488+
}
489+
nodes, err := t.translateApisixRouteStreamBackendResolveGranularity(tctx, utils.NamespacedName(ar), backend)
490+
if err != nil {
491+
return nil, err
492+
}
493+
upstream.Nodes = nodes
494+
return upstream, nil
495+
}

internal/adc/translator/apisixupstream.go

Lines changed: 65 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ package translator
2020
import (
2121
"cmp"
2222
"fmt"
23+
"maps"
2324

2425
"github.com/api7/gopkg/pkg/log"
2526
"github.com/pkg/errors"
@@ -33,50 +34,78 @@ import (
3334
"github.com/apache/apisix-ingress-controller/internal/utils"
3435
)
3536

36-
func (t *Translator) translateApisixUpstream(tctx *provider.TranslateContext, au *apiv2.ApisixUpstream) (ups *adc.Upstream, err error) {
37-
ups = adc.NewDefaultUpstream()
38-
for _, f := range []func(*apiv2.ApisixUpstream, *adc.Upstream) error{
39-
patchApisixUpstreamBasics,
37+
func (t *Translator) translateApisixUpstream(tctx *provider.TranslateContext, au *apiv2.ApisixUpstream) (*adc.Upstream, error) {
38+
return t.translateApisixUpstreamForPort(tctx, au, nil)
39+
}
40+
41+
func (t *Translator) translateApisixUpstreamForPort(tctx *provider.TranslateContext, au *apiv2.ApisixUpstream, port *int32) (*adc.Upstream, error) {
42+
log.Debugw("translating ApisixUpstream", zap.Any("apisixupstream", au), zap.Int32p("port", port))
43+
44+
ups := adc.NewDefaultUpstream()
45+
ups.Name = composeExternalUpstreamName(au)
46+
maps.Copy(ups.Labels, au.Labels)
47+
48+
// translateApisixUpstreamConfig translates the core upstream configuration fields
49+
// from au.Spec.ApisixUpstreamConfig into the ADC upstream.
50+
//
51+
// Note: ExternalNodes is not part of ApisixUpstreamConfig but a separate field
52+
// on ApisixUpstreamSpec, so it is handled separately in translateApisixUpstreamExternalNodes.
53+
if err := translateApisixUpstreamConfig(tctx, &au.Spec.ApisixUpstreamConfig, ups); err != nil {
54+
return nil, err
55+
}
56+
if err := translateApisixUpstreamExternalNodes(tctx, au, ups); err != nil {
57+
return nil, err
58+
}
59+
60+
// If PortLevelSettings is configured and a specific port is provided,
61+
// apply the ApisixUpstreamConfig for the matching port to the upstream.
62+
if len(au.Spec.PortLevelSettings) > 0 && port != nil {
63+
for _, pls := range au.Spec.PortLevelSettings {
64+
if pls.Port != *port {
65+
continue
66+
}
67+
if err := translateApisixUpstreamConfig(tctx, &pls.ApisixUpstreamConfig, ups); err != nil {
68+
return nil, err
69+
}
70+
}
71+
}
72+
73+
log.Debugw("translated ApisixUpstream", zap.Any("upstream", ups))
74+
75+
return ups, nil
76+
}
77+
78+
func translateApisixUpstreamConfig(tctx *provider.TranslateContext, config *apiv2.ApisixUpstreamConfig, ups *adc.Upstream) (err error) {
79+
for _, f := range []func(*apiv2.ApisixUpstreamConfig, *adc.Upstream) error{
4080
translateApisixUpstreamScheme,
4181
translateApisixUpstreamLoadBalancer,
4282
translateApisixUpstreamRetriesAndTimeout,
4383
translateApisixUpstreamPassHost,
4484
translateUpstreamHealthCheck,
4585
translateUpstreamDiscovery,
4686
} {
47-
if err = f(au, ups); err != nil {
87+
if err = f(config, ups); err != nil {
4888
return
4989
}
5090
}
51-
for _, f := range []func(*provider.TranslateContext, *apiv2.ApisixUpstream, *adc.Upstream) error{
91+
for _, f := range []func(*provider.TranslateContext, *apiv2.ApisixUpstreamConfig, *adc.Upstream) error{
5292
translateApisixUpstreamClientTLS,
53-
translateApisixUpstreamExternalNodes,
5493
} {
55-
if err = f(tctx, au, ups); err != nil {
94+
if err = f(tctx, config, ups); err != nil {
5695
return
5796
}
5897
}
5998

60-
log.Debugw("translated ApisixUpstream", zap.Any("upstream", ups),
61-
zap.String("namespace", au.Namespace), zap.String("name", au.Name))
6299
return
63100
}
64101

65-
func patchApisixUpstreamBasics(au *apiv2.ApisixUpstream, ups *adc.Upstream) error {
66-
ups.Name = composeExternalUpstreamName(au)
67-
for k, v := range au.Labels {
68-
ups.Labels[k] = v
69-
}
102+
func translateApisixUpstreamScheme(config *apiv2.ApisixUpstreamConfig, ups *adc.Upstream) error {
103+
ups.Scheme = cmp.Or(config.Scheme, apiv2.SchemeHTTP)
70104
return nil
71105
}
72106

73-
func translateApisixUpstreamScheme(au *apiv2.ApisixUpstream, ups *adc.Upstream) error {
74-
ups.Scheme = cmp.Or(au.Spec.Scheme, apiv2.SchemeHTTP)
75-
return nil
76-
}
77-
78-
func translateApisixUpstreamLoadBalancer(au *apiv2.ApisixUpstream, ups *adc.Upstream) error {
79-
lb := au.Spec.LoadBalancer
107+
func translateApisixUpstreamLoadBalancer(config *apiv2.ApisixUpstreamConfig, ups *adc.Upstream) error {
108+
lb := config.LoadBalancer
80109
if lb == nil || lb.Type == "" {
81110
ups.Type = apiv2.LbRoundRobin
82111
return nil
@@ -107,9 +136,9 @@ func translateApisixUpstreamLoadBalancer(au *apiv2.ApisixUpstream, ups *adc.Upst
107136
return nil
108137
}
109138

110-
func translateApisixUpstreamRetriesAndTimeout(au *apiv2.ApisixUpstream, ups *adc.Upstream) error {
111-
retries := au.Spec.Retries
112-
timeout := au.Spec.Timeout
139+
func translateApisixUpstreamRetriesAndTimeout(config *apiv2.ApisixUpstreamConfig, ups *adc.Upstream) error {
140+
retries := config.Retries
141+
timeout := config.Timeout
113142

114143
if retries != nil && *retries < 0 {
115144
return errors.New("invalid value retries")
@@ -144,15 +173,15 @@ func translateApisixUpstreamRetriesAndTimeout(au *apiv2.ApisixUpstream, ups *adc
144173
return nil
145174
}
146175

147-
func translateApisixUpstreamClientTLS(tctx *provider.TranslateContext, au *apiv2.ApisixUpstream, ups *adc.Upstream) error {
148-
if au.Spec.TLSSecret == nil {
176+
func translateApisixUpstreamClientTLS(tctx *provider.TranslateContext, config *apiv2.ApisixUpstreamConfig, ups *adc.Upstream) error {
177+
if config.TLSSecret == nil {
149178
return nil
150179
}
151180

152181
var (
153182
secretNN = types.NamespacedName{
154-
Namespace: au.Spec.TLSSecret.Namespace,
155-
Name: au.Spec.TLSSecret.Name,
183+
Namespace: config.TLSSecret.Namespace,
184+
Name: config.TLSSecret.Name,
156185
}
157186
)
158187
secret, ok := tctx.Secrets[secretNN]
@@ -173,9 +202,9 @@ func translateApisixUpstreamClientTLS(tctx *provider.TranslateContext, au *apiv2
173202
return nil
174203
}
175204

176-
func translateApisixUpstreamPassHost(au *apiv2.ApisixUpstream, ups *adc.Upstream) error {
177-
ups.PassHost = au.Spec.PassHost
178-
ups.UpstreamHost = au.Spec.UpstreamHost
205+
func translateApisixUpstreamPassHost(config *apiv2.ApisixUpstreamConfig, ups *adc.Upstream) error {
206+
ups.PassHost = config.PassHost
207+
ups.UpstreamHost = config.UpstreamHost
179208

180209
return nil
181210
}
@@ -259,11 +288,8 @@ func translateApisixUpstreamExternalNodesService(tctx *provider.TranslateContext
259288
return nil
260289
}
261290

262-
func translateUpstreamHealthCheck(au *apiv2.ApisixUpstream, ups *adc.Upstream) error {
263-
if au == nil {
264-
return nil
265-
}
266-
healcheck := au.Spec.HealthCheck
291+
func translateUpstreamHealthCheck(config *apiv2.ApisixUpstreamConfig, ups *adc.Upstream) error {
292+
healcheck := config.HealthCheck
267293
if healcheck == nil || (healcheck.Passive == nil && healcheck.Active == nil) {
268294
return nil
269295
}
@@ -346,8 +372,8 @@ func translateUpstreamPassiveHealthCheck(config *apiv2.PassiveHealthCheck) *adc.
346372
return &passive
347373
}
348374

349-
func translateUpstreamDiscovery(au *apiv2.ApisixUpstream, ups *adc.Upstream) error {
350-
discovery := au.Spec.Discovery
375+
func translateUpstreamDiscovery(config *apiv2.ApisixUpstreamConfig, ups *adc.Upstream) error {
376+
discovery := config.Discovery
351377
if discovery == nil {
352378
return nil
353379
}

0 commit comments

Comments
 (0)