Skip to content

Commit bffcdc0

Browse files
committed
feat: support externalname services
1 parent 3ae2b1f commit bffcdc0

9 files changed

+270
-188
lines changed

pkg/backend/endpoint_resolver.go

Lines changed: 55 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package backend
33
import (
44
"context"
55
"fmt"
6+
"net"
67

78
awssdk "github.com/aws/aws-sdk-go-v2/aws"
89
"github.com/go-logr/logr"
@@ -27,12 +28,17 @@ var ErrNotFound = errors.New("backend not found")
2728
type EndpointResolver interface {
2829
// ResolvePodEndpoints will resolve endpoints backed by pods directly.
2930
// returns resolved podEndpoints and whether there are unready endpoints that can potentially turn ready in future reconciles.
30-
ResolvePodEndpoints(ctx context.Context, svcKey types.NamespacedName, port intstr.IntOrString,
31-
opts ...EndpointResolveOption) ([]PodEndpoint, bool, error)
31+
ResolvePodEndpoints(ctx context.Context, svckey types.NamespacedName, svc *corev1.Service, port intstr.IntOrString, opts ...EndpointResolveOption) ([]IpEndpoint, bool, error)
3232

3333
// ResolveNodePortEndpoints will resolve endpoints backed by nodePort.
3434
ResolveNodePortEndpoints(ctx context.Context, svcKey types.NamespacedName, port intstr.IntOrString,
3535
opts ...EndpointResolveOption) ([]NodePortEndpoint, error)
36+
37+
// FindService finds a k8s service
38+
FindService(ctx context.Context, svcKey types.NamespacedName) (*corev1.Service, error)
39+
40+
// ResolveExternalNameEndpoints will resolve external name using dns
41+
ResolveExternalNameEndpoints(ctx context.Context, svc *corev1.Service, port intstr.IntOrString) ([]IpEndpoint, error)
3642
}
3743

3844
// NewDefaultEndpointResolver constructs new defaultEndpointResolver
@@ -42,6 +48,7 @@ func NewDefaultEndpointResolver(k8sClient client.Client, podInfoRepo k8s.PodInfo
4248
podInfoRepo: podInfoRepo,
4349
failOpenEnabled: failOpenEnabled,
4450
endpointSliceEnabled: endpointSliceEnabled,
51+
dnsResolver: net.DefaultResolver,
4552
logger: logger,
4653
}
4754
}
@@ -58,13 +65,34 @@ type defaultEndpointResolver struct {
5865
// [Pod Endpoint] whether to use endpointSlice instead of endpoints
5966
endpointSliceEnabled bool
6067
logger logr.Logger
68+
// dnsResolver to use for resolving external names
69+
dnsResolver dnsResolver
70+
}
71+
72+
type dnsResolver interface {
73+
LookupHost(ctx context.Context, host string) (addrs []string, err error)
74+
}
75+
76+
func (r *defaultEndpointResolver) ResolveExternalNameEndpoints(ctx context.Context, svc *corev1.Service, port intstr.IntOrString) ([]IpEndpoint, error) {
77+
if port.Type == intstr.String {
78+
return nil, fmt.Errorf("port of target group must be numeric for external name")
79+
}
80+
addrs, err := r.dnsResolver.LookupHost(ctx, svc.Spec.ExternalName)
81+
if err != nil {
82+
return nil, err
83+
}
84+
endpoints := make([]IpEndpoint, len(addrs))
85+
for i, ip := range addrs {
86+
endpoints[i] = IpEndpoint{IP: ip, Port: port.IntVal}
87+
}
88+
return endpoints, nil
6189
}
6290

63-
func (r *defaultEndpointResolver) ResolvePodEndpoints(ctx context.Context, svcKey types.NamespacedName, port intstr.IntOrString, opts ...EndpointResolveOption) ([]PodEndpoint, bool, error) {
91+
func (r *defaultEndpointResolver) ResolvePodEndpoints(ctx context.Context, svcKey types.NamespacedName, svc *corev1.Service, port intstr.IntOrString, opts ...EndpointResolveOption) ([]IpEndpoint, bool, error) {
6492
resolveOpts := defaultEndpointResolveOptions()
6593
resolveOpts.ApplyOptions(opts)
6694

67-
_, svcPort, err := r.findServiceAndServicePort(ctx, svcKey, port)
95+
_, svcPort, err := r.findServicePort(svc, port)
6896
if err != nil {
6997
return nil, false, err
7098
}
@@ -140,9 +168,9 @@ func (r *defaultEndpointResolver) computeServiceEndpointsData(ctx context.Contex
140168
return endpointsDataList, nil
141169
}
142170

143-
func (r *defaultEndpointResolver) resolvePodEndpointsWithEndpointsData(ctx context.Context, svcKey types.NamespacedName, svcPort corev1.ServicePort, endpointsDataList []EndpointsData, podReadinessGates []corev1.PodConditionType) ([]PodEndpoint, bool, error) {
144-
var readyPodEndpoints []PodEndpoint
145-
var unknownPodEndpoints []PodEndpoint
171+
func (r *defaultEndpointResolver) resolvePodEndpointsWithEndpointsData(ctx context.Context, svcKey types.NamespacedName, svcPort corev1.ServicePort, endpointsDataList []EndpointsData, podReadinessGates []corev1.PodConditionType) ([]IpEndpoint, bool, error) {
172+
var readyPodEndpoints []IpEndpoint
173+
var unknownPodEndpoints []IpEndpoint
146174
containsPotentialReadyEndpoints := false
147175

148176
for _, epsData := range endpointsDataList {
@@ -175,7 +203,7 @@ func (r *defaultEndpointResolver) resolvePodEndpointsWithEndpointsData(ctx conte
175203
continue
176204
}
177205

178-
podEndpoint := buildPodEndpoint(pod, epAddr, epPort)
206+
podEndpoint := buildPodEndpoint(&pod, epAddr, epPort)
179207
// Recommendation from Kubernetes is to consider unknown ready status as ready (ready == nil)
180208
if ep.Conditions.Ready == nil || *ep.Conditions.Ready {
181209
readyPodEndpoints = append(readyPodEndpoints, podEndpoint)
@@ -220,13 +248,14 @@ func (r *defaultEndpointResolver) resolvePodEndpointsWithEndpointsData(ctx conte
220248
}
221249

222250
func (r *defaultEndpointResolver) findServiceAndServicePort(ctx context.Context, svcKey types.NamespacedName, port intstr.IntOrString) (*corev1.Service, corev1.ServicePort, error) {
223-
svc := &corev1.Service{}
224-
if err := r.k8sClient.Get(ctx, svcKey, svc); err != nil {
225-
if apierrors.IsNotFound(err) {
226-
return nil, corev1.ServicePort{}, fmt.Errorf("%w: %v", ErrNotFound, err.Error())
227-
}
251+
svc, err := r.FindService(ctx, svcKey)
252+
if err != nil {
228253
return nil, corev1.ServicePort{}, err
229254
}
255+
return r.findServicePort(svc, port)
256+
}
257+
258+
func (r *defaultEndpointResolver) findServicePort(svc *corev1.Service, port intstr.IntOrString) (*corev1.Service, corev1.ServicePort, error) {
230259
svcPort, err := k8s.LookupServicePort(svc, port)
231260
if err != nil {
232261
return nil, corev1.ServicePort{}, fmt.Errorf("%w: %v", ErrNotFound, err.Error())
@@ -235,6 +264,17 @@ func (r *defaultEndpointResolver) findServiceAndServicePort(ctx context.Context,
235264
return svc, svcPort, nil
236265
}
237266

267+
func (r *defaultEndpointResolver) FindService(ctx context.Context, svcKey types.NamespacedName) (*corev1.Service, error) {
268+
svc := &corev1.Service{}
269+
if err := r.k8sClient.Get(ctx, svcKey, svc); err != nil {
270+
if apierrors.IsNotFound(err) {
271+
return nil, fmt.Errorf("%w: %v", ErrNotFound, err.Error())
272+
}
273+
return nil, err
274+
}
275+
return svc, nil
276+
}
277+
238278
// filterNodesByReadyConditionStatus will filter out nodes that matches specified ready condition status
239279
func filterNodesByReadyConditionStatus(nodes []*corev1.Node, readyCondStatus corev1.ConditionStatus) []*corev1.Node {
240280
var nodesWithMatchingReadyStatus []*corev1.Node
@@ -287,8 +327,8 @@ func buildEndpointsDataFromEndpointSliceList(epsList *discovery.EndpointSliceLis
287327
return endpointsDataList
288328
}
289329

290-
func buildPodEndpoint(pod k8s.PodInfo, epAddr string, port int32) PodEndpoint {
291-
return PodEndpoint{
330+
func buildPodEndpoint(pod *k8s.PodInfo, epAddr string, port int32) IpEndpoint {
331+
return IpEndpoint{
292332
IP: epAddr,
293333
Port: port,
294334
Pod: pod,

0 commit comments

Comments
 (0)