Skip to content

Commit 298943b

Browse files
authored
feat: support proxy external service (#107)
1 parent 5c3eb3c commit 298943b

File tree

6 files changed

+168
-59
lines changed

6 files changed

+168
-59
lines changed

internal/controller/httproute_controller.go

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -397,14 +397,20 @@ func (r *HTTPRouteReconciler) processHTTPRouteBackendRefs(tctx *provider.Transla
397397
continue
398398
}
399399

400-
var service corev1.Service
401-
if err := r.Get(tctx, client.ObjectKey{
400+
serviceNS := types.NamespacedName{
402401
Namespace: namespace,
403402
Name: name,
404-
}, &service); err != nil {
403+
}
404+
405+
var service corev1.Service
406+
if err := r.Get(tctx, serviceNS, &service); err != nil {
405407
terr = err
406408
continue
407409
}
410+
if service.Spec.Type == corev1.ServiceTypeExternalName {
411+
tctx.Services[serviceNS] = &service
412+
return nil
413+
}
408414

409415
portExists := false
410416
for _, port := range service.Spec.Ports {
@@ -417,10 +423,7 @@ func (r *HTTPRouteReconciler) processHTTPRouteBackendRefs(tctx *provider.Transla
417423
terr = fmt.Errorf("port %d not found in service %s", *backend.Port, name)
418424
continue
419425
}
420-
tctx.Services[client.ObjectKey{
421-
Namespace: namespace,
422-
Name: name,
423-
}] = &service
426+
tctx.Services[serviceNS] = &service
424427

425428
endpointSliceList := new(discoveryv1.EndpointSliceList)
426429
if err := r.List(tctx, endpointSliceList,
@@ -434,11 +437,7 @@ func (r *HTTPRouteReconciler) processHTTPRouteBackendRefs(tctx *provider.Transla
434437
continue
435438
}
436439

437-
tctx.EndpointSlices[client.ObjectKey{
438-
Namespace: namespace,
439-
Name: name,
440-
}] = endpointSliceList.Items
441-
440+
tctx.EndpointSlices[serviceNS] = endpointSliceList.Items
442441
}
443442
return terr
444443
}

internal/controller/ingress_controller.go

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -522,17 +522,23 @@ func (r *IngressReconciler) processBackends(tctx *provider.TranslateContext, ing
522522
func (r *IngressReconciler) processBackendService(tctx *provider.TranslateContext, namespace string, backendService *networkingv1.IngressServiceBackend) error {
523523
// get the service
524524
var service corev1.Service
525-
if err := r.Get(tctx, client.ObjectKey{
525+
serviceNS := types.NamespacedName{
526526
Namespace: namespace,
527527
Name: backendService.Name,
528-
}, &service); err != nil {
528+
}
529+
if err := r.Get(tctx, serviceNS, &service); err != nil {
529530
if client.IgnoreNotFound(err) == nil {
530531
r.Log.Info("service not found", "namespace", namespace, "name", backendService.Name)
531532
return nil
532533
}
533534
return err
534535
}
535536

537+
if service.Spec.Type == corev1.ServiceTypeExternalName {
538+
tctx.Services[serviceNS] = &service
539+
return nil
540+
}
541+
536542
// verify if the port exists
537543
var portExists bool
538544
if backendService.Port.Number != 0 {
@@ -570,16 +576,8 @@ func (r *IngressReconciler) processBackendService(tctx *provider.TranslateContex
570576
}
571577

572578
// save the endpoint slices to the translate context
573-
tctx.EndpointSlices[client.ObjectKey{
574-
Namespace: namespace,
575-
Name: backendService.Name,
576-
}] = endpointSliceList.Items
577-
578-
tctx.Services[client.ObjectKey{
579-
Namespace: namespace,
580-
Name: backendService.Name,
581-
}] = &service
582-
579+
tctx.EndpointSlices[serviceNS] = endpointSliceList.Items
580+
tctx.Services[serviceNS] = &service
583581
return nil
584582
}
585583

internal/provider/adc/translator/httproute.go

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,17 @@ import (
55
"fmt"
66
"strings"
77

8+
"github.com/api7/gopkg/pkg/log"
89
"github.com/pkg/errors"
910
"go.uber.org/zap"
11+
corev1 "k8s.io/api/core/v1"
1012
discoveryv1 "k8s.io/api/discovery/v1"
1113
"k8s.io/apimachinery/pkg/types"
1214
"k8s.io/utils/ptr"
1315
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
1416

15-
"github.com/api7/api7-ingress-controller/api/v1alpha1"
16-
"github.com/api7/gopkg/pkg/log"
17-
1817
adctypes "github.com/api7/api7-ingress-controller/api/adc"
18+
"github.com/api7/api7-ingress-controller/api/v1alpha1"
1919
"github.com/api7/api7-ingress-controller/internal/controller/label"
2020
"github.com/api7/api7-ingress-controller/internal/id"
2121
"github.com/api7/api7-ingress-controller/internal/provider"
@@ -289,16 +289,33 @@ func (t *Translator) translateEndpointSlice(weight int, endpointSlices []discove
289289
}
290290

291291
func (t *Translator) translateBackendRef(tctx *provider.TranslateContext, ref gatewayv1.BackendRef) adctypes.UpstreamNodes {
292-
endpointSlices := tctx.EndpointSlices[types.NamespacedName{
293-
Namespace: string(*ref.Namespace),
294-
Name: string(ref.Name),
295-
}]
296-
297292
weight := 1
293+
port := 80
298294
if ref.Weight != nil {
299295
weight = int(*ref.Weight)
300296
}
297+
if ref.Port != nil {
298+
port = int(*ref.Port)
299+
}
300+
key := types.NamespacedName{
301+
Namespace: string(*ref.Namespace),
302+
Name: string(ref.Name),
303+
}
304+
service, ok := tctx.Services[key]
305+
if !ok {
306+
return adctypes.UpstreamNodes{}
307+
}
301308

309+
if service.Spec.Type == corev1.ServiceTypeExternalName {
310+
return adctypes.UpstreamNodes{
311+
{
312+
Host: service.Spec.ExternalName,
313+
Port: port,
314+
Weight: weight,
315+
},
316+
}
317+
}
318+
endpointSlices := tctx.EndpointSlices[key]
302319
return t.translateEndpointSlice(weight, endpointSlices)
303320
}
304321

internal/provider/adc/translator/ingress.go

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -105,12 +105,7 @@ func (t *Translator) TranslateIngress(tctx *provider.TranslateContext, obj *netw
105105

106106
// get the EndpointSlice of the backend service
107107
backendService := path.Backend.Service
108-
var endpointSlices []discoveryv1.EndpointSlice
109108
if backendService != nil {
110-
endpointSlices = tctx.EndpointSlices[types.NamespacedName{
111-
Namespace: obj.Namespace,
112-
Name: backendService.Name,
113-
}]
114109
backendRef := convertBackendRef(obj.Namespace, backendService.Name, "Service")
115110
t.AttachBackendTrafficPolicyToUpstream(backendRef, tctx.BackendTrafficPolicies, upstream)
116111
}
@@ -131,28 +126,39 @@ func (t *Translator) TranslateIngress(tctx *provider.TranslateContext, obj *netw
131126
if getService == nil {
132127
continue
133128
}
134-
135-
var getServicePort *corev1.ServicePort
136-
for _, port := range getService.Spec.Ports {
137-
port := port
138-
if servicePort > 0 && port.Port == servicePort {
139-
getServicePort = &port
140-
break
129+
if getService.Spec.Type == corev1.ServiceTypeExternalName {
130+
defaultServicePort := 80
131+
if servicePort > 0 {
132+
defaultServicePort = int(servicePort)
141133
}
142-
if servicePortName != "" && port.Name == servicePortName {
143-
getServicePort = &port
144-
break
134+
upstream.Nodes = adctypes.UpstreamNodes{
135+
{
136+
Host: getService.Spec.ExternalName,
137+
Port: defaultServicePort,
138+
Weight: 1,
139+
},
140+
}
141+
} else {
142+
var getServicePort *corev1.ServicePort
143+
for _, port := range getService.Spec.Ports {
144+
port := port
145+
if servicePort > 0 && port.Port == servicePort {
146+
getServicePort = &port
147+
break
148+
}
149+
if servicePortName != "" && port.Name == servicePortName {
150+
getServicePort = &port
151+
break
152+
}
153+
}
154+
endpointSlices := tctx.EndpointSlices[types.NamespacedName{
155+
Namespace: obj.Namespace,
156+
Name: backendService.Name,
157+
}]
158+
// convert the EndpointSlice to upstream nodes
159+
if len(endpointSlices) > 0 {
160+
upstream.Nodes = t.translateEndpointSliceForIngress(1, endpointSlices, getServicePort)
145161
}
146-
}
147-
148-
// convert the EndpointSlice to upstream nodes
149-
if len(endpointSlices) > 0 {
150-
upstream.Nodes = t.translateEndpointSliceForIngress(1, endpointSlices, getServicePort)
151-
}
152-
153-
// if there is no upstream node, create a placeholder node
154-
if len(upstream.Nodes) == 0 {
155-
upstream.Nodes = adctypes.UpstreamNodes{}
156162
}
157163

158164
service.Upstream = upstream

test/e2e/gatewayapi/httproute.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,33 @@ spec:
365365
})
366366

367367
Context("HTTPRoute Base", func() {
368+
var httprouteWithExternalName = `
369+
apiVersion: v1
370+
kind: Service
371+
metadata:
372+
name: httpbin-external-domain
373+
spec:
374+
type: ExternalName
375+
externalName: httpbin.org
376+
---
377+
apiVersion: gateway.networking.k8s.io/v1
378+
kind: HTTPRoute
379+
metadata:
380+
name: httpbin
381+
spec:
382+
parentRefs:
383+
- name: api7ee
384+
hostnames:
385+
- httpbin.external
386+
rules:
387+
- matches:
388+
- path:
389+
type: Exact
390+
value: /get
391+
backendRefs:
392+
- name: httpbin-external-domain
393+
port: 80
394+
`
368395
var exactRouteByGet = `
369396
apiVersion: gateway.networking.k8s.io/v1
370397
kind: HTTPRoute
@@ -437,6 +464,18 @@ spec:
437464
Expect().
438465
Status(404)
439466
})
467+
468+
It("Proxy External Service", func() {
469+
By("create HTTPRoute")
470+
ResourceApplied("HTTPRoute", "httpbin", httprouteWithExternalName, 1)
471+
472+
By("checking the external service response")
473+
s.NewAPISIXClient().
474+
GET("/get").
475+
WithHost("httpbin.external").
476+
Expect().
477+
Status(200)
478+
})
440479
})
441480
Context("HTTPRoute Rule Match", func() {
442481
var exactRouteByGet = `

test/e2e/ingress/ingress.go

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,11 +158,36 @@ spec:
158158
number: 80
159159
`
160160

161+
var ingressWithExternalName = `
162+
apiVersion: v1
163+
kind: Service
164+
metadata:
165+
name: httpbin-external-domain
166+
spec:
167+
type: ExternalName
168+
externalName: httpbin.org
169+
---
170+
apiVersion: networking.k8s.io/v1
171+
kind: Ingress
172+
metadata:
173+
name: api7-ingress-default
174+
spec:
175+
rules:
176+
- host: httpbin.external
177+
http:
178+
paths:
179+
- path: /
180+
pathType: Prefix
181+
backend:
182+
service:
183+
name: httpbin-external-domain
184+
port:
185+
number: 80
186+
`
187+
161188
It("Test IngressClass Selection", func() {
162189
By("create GatewayProxy")
163190
gatewayProxy := fmt.Sprintf(gatewayProxyYaml, framework.DashboardTLSEndpoint, s.AdminKey())
164-
165-
By("create GatewayProxy")
166191
err := s.CreateResourceFromStringWithNamespace(gatewayProxy, "default")
167192
Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy")
168193
time.Sleep(5 * time.Second)
@@ -184,6 +209,31 @@ spec:
184209
Expect().
185210
Status(200)
186211
})
212+
213+
It("Proxy External Service", func() {
214+
By("create GatewayProxy")
215+
gatewayProxy := fmt.Sprintf(gatewayProxyYaml, framework.DashboardTLSEndpoint, s.AdminKey())
216+
err := s.CreateResourceFromStringWithNamespace(gatewayProxy, "default")
217+
Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy")
218+
time.Sleep(5 * time.Second)
219+
220+
By("create Default IngressClass")
221+
err = s.CreateResourceFromStringWithNamespace(defaultIngressClass, "")
222+
Expect(err).NotTo(HaveOccurred(), "creating Default IngressClass")
223+
time.Sleep(5 * time.Second)
224+
225+
By("create Ingress")
226+
err = s.CreateResourceFromString(ingressWithExternalName)
227+
Expect(err).NotTo(HaveOccurred(), "creating Ingress without IngressClass")
228+
time.Sleep(5 * time.Second)
229+
230+
By("checking the external service response")
231+
s.NewAPISIXClient().
232+
GET("/get").
233+
WithHost("httpbin.external").
234+
Expect().
235+
Status(200)
236+
})
187237
})
188238

189239
Context("IngressClass with GatewayProxy", func() {

0 commit comments

Comments
 (0)