Skip to content

Commit c26829f

Browse files
AlinsRanbackport-bot[bot]
authored andcommitted
feat: support resolve svc.ports[].appProtocol (#2601)
(cherry picked from commit f28b34292be45587184d9faa8cb5bfc477d5d1b8)
1 parent 6b98bdb commit c26829f

File tree

16 files changed

+1282
-147
lines changed

16 files changed

+1282
-147
lines changed

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,11 @@ GO_LDFLAGS ?= "-X=$(VERSYM)=$(VERSION) -X=$(GITSHASYM)=$(GITSHA) -X=$(BUILDOSSYM
5959
# gateway-api
6060
GATEAY_API_VERSION ?= v1.3.0
6161
## https://github.com/kubernetes-sigs/gateway-api/blob/v1.3.0/pkg/features/httproute.go
62+
<<<<<<< HEAD
6263
SUPPORTED_EXTENDED_FEATURES = "HTTPRouteDestinationPortMatching,HTTPRouteMethodMatching,HTTPRoutePortRedirect,HTTPRouteRequestMirror,HTTPRouteSchemeRedirect,GatewayAddressEmpty,HTTPRouteResponseHeaderModification,GatewayPort8080"
64+
=======
65+
SUPPORTED_EXTENDED_FEATURES = "HTTPRouteDestinationPortMatching,HTTPRouteMethodMatching,HTTPRoutePortRedirect,HTTPRouteRequestMirror,HTTPRouteSchemeRedirect,GatewayAddressEmpty,HTTPRouteResponseHeaderModification,GatewayPort8080,HTTPRouteHostRewrite,HTTPRouteQueryParamMatching,HTTPRoutePathRewrite,HTTPRouteBackendProtocolWebSocket"
66+
>>>>>>> f28b3429 (feat: support resolve svc.ports[].appProtocol (#2601))
6367
CONFORMANCE_TEST_REPORT_OUTPUT ?= $(DIR)/apisix-ingress-controller-conformance-report.yaml
6468
## https://github.com/kubernetes-sigs/gateway-api/blob/v1.3.0/conformance/utils/suite/profiles.go
6569
CONFORMANCE_PROFILES ?= GATEWAY-HTTP,GATEWAY-GRPC

api/v2/apisixroute_types.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ type ApisixRouteHTTP struct {
9494

9595
// Websocket enables or disables websocket support for this route.
9696
// +kubebuilder:validation:Optional
97-
Websocket bool `json:"websocket" yaml:"websocket"`
97+
Websocket *bool `json:"websocket" yaml:"websocket"`
9898
// PluginConfigName specifies the name of the plugin config to apply.
9999
PluginConfigName string `json:"plugin_config_name,omitempty" yaml:"plugin_config_name,omitempty"`
100100
// PluginConfigNamespace specifies the namespace of the plugin config.

api/v2/zz_generated.deepcopy.go

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/adc/translator/apisixroute.go

Lines changed: 142 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,12 @@ import (
2525

2626
"github.com/api7/gopkg/pkg/log"
2727
"github.com/pkg/errors"
28+
<<<<<<< HEAD
2829
"go.uber.org/zap"
2930
v1 "k8s.io/api/core/v1"
31+
=======
32+
corev1 "k8s.io/api/core/v1"
33+
>>>>>>> f28b3429 (feat: support resolve svc.ports[].appProtocol (#2601))
3034
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3135
"k8s.io/apimachinery/pkg/types"
3236
"k8s.io/apimachinery/pkg/util/intstr"
@@ -72,10 +76,16 @@ func (t *Translator) translateHTTPRule(tctx *provider.TranslateContext, ar *apiv
7276
return nil, err
7377
}
7478

79+
var enableWebsocket *bool
7580
service := t.buildService(ar, rule, ruleIndex)
81+
<<<<<<< HEAD
7682
t.buildRoute(ar, service, rule, plugins, timeout, vars)
7783
t.buildUpstream(tctx, service, ar, rule, ruleIndex)
7884

85+
=======
86+
t.buildUpstream(tctx, service, ar, rule, &enableWebsocket)
87+
t.buildRoute(ar, service, rule, plugins, timeout, vars, &enableWebsocket)
88+
>>>>>>> f28b3429 (feat: support resolve svc.ports[].appProtocol (#2601))
7989
return service, nil
8090
}
8191

@@ -141,7 +151,7 @@ func (t *Translator) loadRoutePlugins(tctx *provider.TranslateContext, ar *apiv2
141151
}
142152
}
143153

144-
func (t *Translator) buildPluginConfig(plugin apiv2.ApisixRoutePlugin, namespace string, secrets map[types.NamespacedName]*v1.Secret) map[string]any {
154+
func (t *Translator) buildPluginConfig(plugin apiv2.ApisixRoutePlugin, namespace string, secrets map[types.NamespacedName]*corev1.Secret) map[string]any {
145155
config := make(map[string]any)
146156
if len(plugin.Config.Raw) > 0 {
147157
if err := json.Unmarshal(plugin.Config.Raw, &config); err != nil {
@@ -181,13 +191,16 @@ func (t *Translator) addAuthenticationPlugins(rule apiv2.ApisixRouteHTTP, plugin
181191
}
182192
}
183193

184-
func (t *Translator) buildRoute(ar *apiv2.ApisixRoute, service *adc.Service, rule apiv2.ApisixRouteHTTP, plugins adc.Plugins, timeout *adc.Timeout, vars adc.Vars) {
194+
func (t *Translator) buildRoute(ar *apiv2.ApisixRoute, service *adc.Service, rule apiv2.ApisixRouteHTTP, plugins adc.Plugins, timeout *adc.Timeout, vars adc.Vars, enableWebsocket **bool) {
185195
route := adc.NewDefaultRoute()
186196
route.Name = adc.ComposeRouteName(ar.Namespace, ar.Name, rule.Name)
187197
route.ID = id.GenID(route.Name)
188198
route.Desc = "Created by apisix-ingress-controller, DO NOT modify it manually"
189199
route.Labels = label.GenLabel(ar)
190-
route.EnableWebsocket = ptr.To(rule.Websocket)
200+
route.EnableWebsocket = rule.Websocket
201+
if route.EnableWebsocket == nil && *enableWebsocket != nil {
202+
route.EnableWebsocket = *enableWebsocket
203+
}
191204
route.FilterFunc = rule.Match.FilterFunc
192205
route.Hosts = rule.Match.Hosts
193206
route.Methods = rule.Match.Methods
@@ -204,7 +217,11 @@ func (t *Translator) buildRoute(ar *apiv2.ApisixRoute, service *adc.Service, rul
204217
service.Routes = []*adc.Route{route}
205218
}
206219

220+
<<<<<<< HEAD
207221
func (t *Translator) buildUpstream(tctx *provider.TranslateContext, service *adc.Service, ar *apiv2.ApisixRoute, rule apiv2.ApisixRouteHTTP, ruleIndex int) {
222+
=======
223+
func (t *Translator) buildUpstream(tctx *provider.TranslateContext, service *adc.Service, ar *apiv2.ApisixRoute, rule apiv2.ApisixRouteHTTP, enableWebsocket **bool) {
224+
>>>>>>> f28b3429 (feat: support resolve svc.ports[].appProtocol (#2601))
208225
var (
209226
upstreams = make([]*adc.Upstream, 0)
210227
weightedUpstreams = make([]adc.TrafficSplitConfigRuleWeightedUpstream, 0)
@@ -215,9 +232,16 @@ func (t *Translator) buildUpstream(tctx *provider.TranslateContext, service *adc
215232
upstream := adc.NewDefaultUpstream()
216233
// try to get the apisixupstream with the same name as the backend service to be upstream config.
217234
// err is ignored because it does not care about the externalNodes of the apisixupstream.
235+
<<<<<<< HEAD
218236
auNN := types.NamespacedName{Namespace: ar.GetNamespace(), Name: backend.ServiceName}
219237
if au, ok := tctx.Upstreams[auNN]; ok {
220238
upstream, _ = t.translateApisixUpstream(tctx, au)
239+
=======
240+
upstream, err := t.translateApisixRouteHTTPBackend(tctx, ar, backend, enableWebsocket)
241+
if err != nil {
242+
t.Log.Error(err, "failed to translate ApisixRoute backend", "backend", backend)
243+
continue
244+
>>>>>>> f28b3429 (feat: support resolve svc.ports[].appProtocol (#2601))
221245
}
222246

223247
if backend.ResolveGranularity == apiv2.ResolveGranularityService {
@@ -332,7 +356,7 @@ func (t *Translator) buildService(ar *apiv2.ApisixRoute, rule apiv2.ApisixRouteH
332356
return service
333357
}
334358

335-
func getPortFromService(svc *v1.Service, backendSvcPort intstr.IntOrString) (int32, error) {
359+
func getPortFromService(svc *corev1.Service, backendSvcPort intstr.IntOrString) (int32, error) {
336360
var port int32
337361
if backendSvcPort.Type == intstr.Int {
338362
port = int32(backendSvcPort.IntValue())
@@ -352,32 +376,107 @@ func getPortFromService(svc *v1.Service, backendSvcPort intstr.IntOrString) (int
352376
return port, nil
353377
}
354378

379+
<<<<<<< HEAD
355380
func (t *Translator) translateApisixRouteBackendResolveGranularityService(tctx *provider.TranslateContext, arNN types.NamespacedName, backend apiv2.ApisixRouteHTTPBackend) (adc.UpstreamNodes, error) {
381+
=======
382+
func findMatchingServicePort(svc *corev1.Service, backendSvcPort intstr.IntOrString) (*corev1.ServicePort, error) {
383+
var servicePort *corev1.ServicePort
384+
var portNumber int32 = -1
385+
var servicePortName string
386+
switch backendSvcPort.Type {
387+
case intstr.Int:
388+
portNumber = backendSvcPort.IntVal
389+
case intstr.String:
390+
servicePortName = backendSvcPort.StrVal
391+
}
392+
for _, svcPort := range svc.Spec.Ports {
393+
p := svcPort
394+
if p.Port == portNumber || (p.Name != "" && p.Name == servicePortName) {
395+
servicePort = &p
396+
break
397+
}
398+
}
399+
if servicePort == nil {
400+
return nil, errors.Errorf("service port %s not found in service %s", backendSvcPort.String(), svc.Name)
401+
}
402+
403+
return servicePort, nil
404+
}
405+
406+
func (t *Translator) translateApisixRouteHTTPBackend(tctx *provider.TranslateContext, ar *apiv2.ApisixRoute, backend apiv2.ApisixRouteHTTPBackend, enableWebsocket **bool) (*adc.Upstream, error) {
407+
auNN := types.NamespacedName{
408+
Namespace: ar.Namespace,
409+
Name: backend.ServiceName,
410+
}
411+
upstream := adc.NewDefaultUpstream()
412+
if au, ok := tctx.Upstreams[auNN]; ok {
413+
svc := tctx.Services[auNN]
414+
if svc == nil {
415+
return nil, errors.Errorf("service not found, ApisixRoute: %s, Service: %s", utils.NamespacedName(ar).String(), auNN)
416+
}
417+
port, err := getPortFromService(svc, backend.ServicePort)
418+
if err != nil {
419+
return nil, err
420+
}
421+
u, err := t.translateApisixUpstreamForPort(tctx, au, ptr.To(port))
422+
if err != nil {
423+
return nil, err
424+
}
425+
upstream = u
426+
}
427+
var (
428+
err error
429+
nodes adc.UpstreamNodes
430+
protocol string
431+
)
432+
if backend.ResolveGranularity == apiv2.ResolveGranularityService {
433+
nodes, protocol, err = t.translateApisixRouteBackendResolveGranularityService(tctx, auNN, backend)
434+
} else {
435+
nodes, protocol, err = t.translateApisixRouteBackendResolveGranularityEndpoint(tctx, auNN, backend)
436+
}
437+
if err != nil {
438+
return nil, err
439+
}
440+
upstream.Nodes = nodes
441+
if upstream.Scheme == "" {
442+
upstream.Scheme = appProtocolToUpstreamScheme(protocol)
443+
}
444+
if protocol == internaltypes.AppProtocolWS || protocol == internaltypes.AppProtocolWSS {
445+
*enableWebsocket = ptr.To(true)
446+
}
447+
if backend.Weight != nil {
448+
upstream.Labels["meta_weight"] = strconv.FormatInt(int64(*backend.Weight), 10)
449+
}
450+
return upstream, nil
451+
}
452+
453+
func (t *Translator) translateApisixRouteBackendResolveGranularityService(tctx *provider.TranslateContext, arNN types.NamespacedName, backend apiv2.ApisixRouteHTTPBackend) (adc.UpstreamNodes, string, error) {
454+
>>>>>>> f28b3429 (feat: support resolve svc.ports[].appProtocol (#2601))
356455
serviceNN := types.NamespacedName{
357456
Namespace: arNN.Namespace,
358457
Name: backend.ServiceName,
359458
}
360459
svc, ok := tctx.Services[serviceNN]
361460
if !ok {
362-
return nil, errors.Errorf("service not found, ApisixRoute: %s, Service: %s", arNN, serviceNN)
461+
return nil, "", errors.Errorf("service not found, ApisixRoute: %s, Service: %s", arNN, serviceNN)
363462
}
364463
if svc.Spec.ClusterIP == "" {
365-
return nil, errors.Errorf("conflict headless service and backend resolve granularity, ApisixRoute: %s, Service: %s", arNN, serviceNN)
464+
return nil, "", errors.Errorf("conflict headless service and backend resolve granularity, ApisixRoute: %s, Service: %s", arNN, serviceNN)
366465
}
367-
port, err := getPortFromService(svc, backend.ServicePort)
466+
port, err := findMatchingServicePort(svc, backend.ServicePort)
368467
if err != nil {
369-
return nil, err
468+
return nil, "", err
370469
}
371470
return adc.UpstreamNodes{
372471
{
373472
Host: svc.Spec.ClusterIP,
374-
Port: int(port),
473+
Port: int(port.Port),
375474
Weight: *cmp.Or(backend.Weight, ptr.To(apiv2.DefaultWeight)),
376475
},
377-
}, nil
476+
}, ptr.Deref(port.AppProtocol, ""), nil
378477
}
379478

380-
func (t *Translator) translateApisixRouteStreamBackendResolveGranularity(tctx *provider.TranslateContext, arNN types.NamespacedName, backend apiv2.ApisixRouteStreamBackend) (adc.UpstreamNodes, error) {
479+
func (t *Translator) translateApisixRouteStreamBackendResolveGranularity(tctx *provider.TranslateContext, arNN types.NamespacedName, backend apiv2.ApisixRouteStreamBackend) (adc.UpstreamNodes, string, error) {
381480
tsBackend := apiv2.ApisixRouteHTTPBackend{
382481
ServiceName: backend.ServiceName,
383482
ServicePort: backend.ServicePort,
@@ -391,18 +490,18 @@ func (t *Translator) translateApisixRouteStreamBackendResolveGranularity(tctx *p
391490
}
392491
}
393492

394-
func (t *Translator) translateApisixRouteBackendResolveGranularityEndpoint(tctx *provider.TranslateContext, arNN types.NamespacedName, backend apiv2.ApisixRouteHTTPBackend) (adc.UpstreamNodes, error) {
493+
func (t *Translator) translateApisixRouteBackendResolveGranularityEndpoint(tctx *provider.TranslateContext, arNN types.NamespacedName, backend apiv2.ApisixRouteHTTPBackend) (adc.UpstreamNodes, string, error) {
395494
serviceNN := types.NamespacedName{
396495
Namespace: arNN.Namespace,
397496
Name: backend.ServiceName,
398497
}
399498
svc, ok := tctx.Services[serviceNN]
400499
if !ok {
401-
return nil, errors.Errorf("service not found, ApisixRoute: %s, Service: %s", arNN, serviceNN)
500+
return nil, "", errors.Errorf("service not found, ApisixRoute: %s, Service: %s", arNN, serviceNN)
402501
}
403502
port, err := getPortFromService(svc, backend.ServicePort)
404503
if err != nil {
405-
return nil, err
504+
return nil, "", err
406505
}
407506
weight := int32(*cmp.Or(backend.Weight, ptr.To(apiv2.DefaultWeight)))
408507
backendRef := gatewayv1.BackendRef{
@@ -451,3 +550,32 @@ func (t *Translator) translateStreamRule(tctx *provider.TranslateContext, ar *ap
451550
svc.Upstream = upstream
452551
return svc, nil
453552
}
553+
<<<<<<< HEAD
554+
=======
555+
556+
func (t *Translator) translateApisixRouteStreamBackend(tctx *provider.TranslateContext, ar *apiv2.ApisixRoute, backend apiv2.ApisixRouteStreamBackend) (*adc.Upstream, error) {
557+
auNN := types.NamespacedName{Namespace: ar.GetNamespace(), Name: backend.ServiceName}
558+
upstream := adc.NewDefaultUpstream()
559+
if au, ok := tctx.Upstreams[auNN]; ok {
560+
service := tctx.Services[auNN]
561+
if service == nil {
562+
return nil, errors.Errorf("service not found, ApisixRoute: %s, Service: %s", utils.NamespacedName(ar), auNN)
563+
}
564+
port, err := getPortFromService(service, backend.ServicePort)
565+
if err != nil {
566+
return nil, err
567+
}
568+
u, err := t.translateApisixUpstreamForPort(tctx, au, ptr.To(port))
569+
if err != nil {
570+
return nil, err
571+
}
572+
upstream = u
573+
}
574+
nodes, _, err := t.translateApisixRouteStreamBackendResolveGranularity(tctx, utils.NamespacedName(ar), backend)
575+
if err != nil {
576+
return nil, err
577+
}
578+
upstream.Nodes = nodes
579+
return upstream, nil
580+
}
581+
>>>>>>> f28b3429 (feat: support resolve svc.ports[].appProtocol (#2601))

internal/adc/translator/gatewayproxy.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ func (t *Translator) TranslateGatewayProxyToConfig(tctx *provider.TranslateConte
100100
if endpoint == nil {
101101
return nil, nil
102102
}
103-
upstreamNodes, err := t.TranslateBackendRefWithFilter(tctx, gatewayv1.BackendRef{
103+
upstreamNodes, _, err := t.TranslateBackendRefWithFilter(tctx, gatewayv1.BackendRef{
104104
BackendObjectReference: gatewayv1.BackendObjectReference{
105105
Name: gatewayv1.ObjectName(provider.ControlPlane.Service.Name),
106106
Namespace: (*gatewayv1.Namespace)(&gatewayProxy.Namespace),

internal/adc/translator/grpcroute.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ func (t *Translator) TranslateGRPCRoute(tctx *provider.TranslateContext, grpcRou
183183
backend.Namespace = &namespace
184184
}
185185
upstream := adctypes.NewDefaultUpstream()
186-
upNodes, err := t.translateBackendRef(tctx, backend.BackendRef, DefaultEndpointFilter)
186+
upNodes, _, err := t.translateBackendRef(tctx, backend.BackendRef, DefaultEndpointFilter)
187187
if err != nil {
188188
backendErr = err
189189
continue

0 commit comments

Comments
 (0)