Skip to content

Commit 8c92a38

Browse files
committed
feat: support externalname services
fixes #3505
1 parent e5fdc89 commit 8c92a38

File tree

6 files changed

+227
-152
lines changed

6 files changed

+227
-152
lines changed

pkg/backend/endpoint_resolver.go

+55-15
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/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: int64(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 {
@@ -170,7 +198,7 @@ func (r *defaultEndpointResolver) resolvePodEndpointsWithEndpointsData(ctx conte
170198
containsPotentialReadyEndpoints = true
171199
continue
172200
}
173-
podEndpoint := buildPodEndpoint(pod, epAddr, epPort)
201+
podEndpoint := buildPodEndpoint(&pod, epAddr, epPort)
174202
if ep.Conditions.Ready != nil && *ep.Conditions.Ready {
175203
readyPodEndpoints = append(readyPodEndpoints, podEndpoint)
176204
continue
@@ -214,13 +242,14 @@ func (r *defaultEndpointResolver) resolvePodEndpointsWithEndpointsData(ctx conte
214242
}
215243

216244
func (r *defaultEndpointResolver) findServiceAndServicePort(ctx context.Context, svcKey types.NamespacedName, port intstr.IntOrString) (*corev1.Service, corev1.ServicePort, error) {
217-
svc := &corev1.Service{}
218-
if err := r.k8sClient.Get(ctx, svcKey, svc); err != nil {
219-
if apierrors.IsNotFound(err) {
220-
return nil, corev1.ServicePort{}, fmt.Errorf("%w: %v", ErrNotFound, err.Error())
221-
}
245+
svc, err := r.FindService(ctx, svcKey)
246+
if err != nil {
222247
return nil, corev1.ServicePort{}, err
223248
}
249+
return r.findServicePort(svc, port)
250+
}
251+
252+
func (r *defaultEndpointResolver) findServicePort(svc *corev1.Service, port intstr.IntOrString) (*corev1.Service, corev1.ServicePort, error) {
224253
svcPort, err := k8s.LookupServicePort(svc, port)
225254
if err != nil {
226255
return nil, corev1.ServicePort{}, fmt.Errorf("%w: %v", ErrNotFound, err.Error())
@@ -229,6 +258,17 @@ func (r *defaultEndpointResolver) findServiceAndServicePort(ctx context.Context,
229258
return svc, svcPort, nil
230259
}
231260

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

284-
func buildPodEndpoint(pod k8s.PodInfo, epAddr string, port int32) PodEndpoint {
285-
return PodEndpoint{
324+
func buildPodEndpoint(pod *k8s.PodInfo, epAddr string, port int32) IpEndpoint {
325+
return IpEndpoint{
286326
IP: epAddr,
287327
Port: int64(port),
288328
Pod: pod,

0 commit comments

Comments
 (0)