Skip to content

Commit f553585

Browse files
committed
feat: support externalname services
1 parent 8def727 commit f553585

9 files changed

+271
-189
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 {
@@ -171,7 +199,7 @@ func (r *defaultEndpointResolver) resolvePodEndpointsWithEndpointsData(ctx conte
171199
continue
172200
}
173201

174-
podEndpoint := buildPodEndpoint(pod, epAddr, epPort)
202+
podEndpoint := buildPodEndpoint(&pod, epAddr, epPort)
175203
// Recommendation from Kubernetes is to consider unknown ready status as ready (ready == nil)
176204
if ep.Conditions.Ready == nil || *ep.Conditions.Ready {
177205
readyPodEndpoints = append(readyPodEndpoints, podEndpoint)
@@ -216,13 +244,14 @@ func (r *defaultEndpointResolver) resolvePodEndpointsWithEndpointsData(ctx conte
216244
}
217245

218246
func (r *defaultEndpointResolver) findServiceAndServicePort(ctx context.Context, svcKey types.NamespacedName, port intstr.IntOrString) (*corev1.Service, corev1.ServicePort, error) {
219-
svc := &corev1.Service{}
220-
if err := r.k8sClient.Get(ctx, svcKey, svc); err != nil {
221-
if apierrors.IsNotFound(err) {
222-
return nil, corev1.ServicePort{}, fmt.Errorf("%w: %v", ErrNotFound, err.Error())
223-
}
247+
svc, err := r.FindService(ctx, svcKey)
248+
if err != nil {
224249
return nil, corev1.ServicePort{}, err
225250
}
251+
return r.findServicePort(svc, port)
252+
}
253+
254+
func (r *defaultEndpointResolver) findServicePort(svc *corev1.Service, port intstr.IntOrString) (*corev1.Service, corev1.ServicePort, error) {
226255
svcPort, err := k8s.LookupServicePort(svc, port)
227256
if err != nil {
228257
return nil, corev1.ServicePort{}, fmt.Errorf("%w: %v", ErrNotFound, err.Error())
@@ -231,6 +260,17 @@ func (r *defaultEndpointResolver) findServiceAndServicePort(ctx context.Context,
231260
return svc, svcPort, nil
232261
}
233262

263+
func (r *defaultEndpointResolver) FindService(ctx context.Context, svcKey types.NamespacedName) (*corev1.Service, error) {
264+
svc := &corev1.Service{}
265+
if err := r.k8sClient.Get(ctx, svcKey, svc); err != nil {
266+
if apierrors.IsNotFound(err) {
267+
return nil, fmt.Errorf("%w: %v", ErrNotFound, err.Error())
268+
}
269+
return nil, err
270+
}
271+
return svc, nil
272+
}
273+
234274
// filterNodesByReadyConditionStatus will filter out nodes that matches specified ready condition status
235275
func filterNodesByReadyConditionStatus(nodes []*corev1.Node, readyCondStatus corev1.ConditionStatus) []*corev1.Node {
236276
var nodesWithMatchingReadyStatus []*corev1.Node
@@ -283,8 +323,8 @@ func buildEndpointsDataFromEndpointSliceList(epsList *discovery.EndpointSliceLis
283323
return endpointsDataList
284324
}
285325

286-
func buildPodEndpoint(pod k8s.PodInfo, epAddr string, port int32) PodEndpoint {
287-
return PodEndpoint{
326+
func buildPodEndpoint(pod *k8s.PodInfo, epAddr string, port int32) IpEndpoint {
327+
return IpEndpoint{
288328
IP: epAddr,
289329
Port: port,
290330
Pod: pod,

0 commit comments

Comments
 (0)