refactor: handle internal IPv6 addresses consistently with --expose-internal-ipv6 flag throughout all sources

This commit is contained in:
Kevin Valk 2025-05-14 17:15:47 +02:00
parent 7e9f14848e
commit e379fe172e
No known key found for this signature in database
6 changed files with 96 additions and 16 deletions

View File

@ -61,7 +61,7 @@ func TestIsValidIPv6(t *testing.T) {
}{ }{
{"2001:0db8:85a3:0000:0000:8a2e:0370:7334", true}, {"2001:0db8:85a3:0000:0000:8a2e:0370:7334", true},
{"2001:db8:85a3::8a2e:370:7334", true}, {"2001:db8:85a3::8a2e:370:7334", true},
//IPV6 dual, the format is y:y:y:y:y:y:x.x.x.x. //IPv6 dual, the format is y:y:y:y:y:y:x.x.x.x.
{"::ffff:192.168.20.3", true}, {"::ffff:192.168.20.3", true},
{"::1", true}, {"::1", true},
{"::", true}, {"::", true},

View File

@ -159,7 +159,7 @@ func legacyEndpointsFromDNSControllerNodePortService(svc *v1.Service, sc *servic
for _, address := range node.Status.Addresses { for _, address := range node.Status.Addresses {
recordType := suitableType(address.Address) recordType := suitableType(address.Address)
// IPv6 addresses are labeled as NodeInternalIP despite being usable externally as well. // IPv6 addresses are labeled as NodeInternalIP despite being usable externally as well.
if isExternal && (address.Type == v1.NodeExternalIP || (address.Type == v1.NodeInternalIP && recordType == endpoint.RecordTypeAAAA)) { if isExternal && (address.Type == v1.NodeExternalIP || (sc.exposeInternalIPv6 && address.Type == v1.NodeInternalIP && recordType == endpoint.RecordTypeAAAA)) {
endpoints = append(endpoints, endpoint.NewEndpoint(hostname, recordType, address.Address)) endpoints = append(endpoints, endpoint.NewEndpoint(hostname, recordType, address.Address))
} }
if isInternal && address.Type == v1.NodeInternalIP { if isInternal && address.Type == v1.NodeInternalIP {

View File

@ -43,7 +43,7 @@ type nodeSource struct {
nodeInformer coreinformers.NodeInformer nodeInformer coreinformers.NodeInformer
labelSelector labels.Selector labelSelector labels.Selector
excludeUnschedulable bool excludeUnschedulable bool
exposeInternalIPV6 bool exposeInternalIPv6 bool
} }
// NewNodeSource creates a new nodeSource with the given config. // NewNodeSource creates a new nodeSource with the given config.
@ -81,7 +81,7 @@ func NewNodeSource(ctx context.Context, kubeClient kubernetes.Interface, annotat
nodeInformer: nodeInformer, nodeInformer: nodeInformer,
labelSelector: labelSelector, labelSelector: labelSelector,
excludeUnschedulable: excludeUnschedulable, excludeUnschedulable: excludeUnschedulable,
exposeInternalIPV6: exposeInternalIPv6, exposeInternalIPv6: exposeInternalIPv6,
}, nil }, nil
} }
@ -193,7 +193,7 @@ func (ns *nodeSource) nodeAddresses(node *v1.Node) ([]string, error) {
} }
if len(addresses[v1.NodeExternalIP]) > 0 { if len(addresses[v1.NodeExternalIP]) > 0 {
if ns.exposeInternalIPV6 { if ns.exposeInternalIPv6 {
log.Warn(warningMsg) log.Warn(warningMsg)
return append(addresses[v1.NodeExternalIP], internalIpv6Addresses...), nil return append(addresses[v1.NodeExternalIP], internalIpv6Addresses...), nil
} }

View File

@ -65,10 +65,11 @@ type serviceSource struct {
nodeInformer coreinformers.NodeInformer nodeInformer coreinformers.NodeInformer
serviceTypeFilter map[string]struct{} serviceTypeFilter map[string]struct{}
labelSelector labels.Selector labelSelector labels.Selector
exposeInternalIPv6 bool
} }
// NewServiceSource creates a new serviceSource with the given config. // NewServiceSource creates a new serviceSource with the given config.
func NewServiceSource(ctx context.Context, kubeClient kubernetes.Interface, namespace, annotationFilter, fqdnTemplate string, combineFqdnAnnotation bool, compatibility string, publishInternal, publishHostIP, alwaysPublishNotReadyAddresses bool, serviceTypeFilter []string, ignoreHostnameAnnotation bool, labelSelector labels.Selector, resolveLoadBalancerHostname, listenEndpointEvents bool) (Source, error) { func NewServiceSource(ctx context.Context, kubeClient kubernetes.Interface, namespace, annotationFilter, fqdnTemplate string, combineFqdnAnnotation bool, compatibility string, publishInternal, publishHostIP, alwaysPublishNotReadyAddresses bool, serviceTypeFilter []string, ignoreHostnameAnnotation bool, labelSelector labels.Selector, resolveLoadBalancerHostname, listenEndpointEvents bool, exposeInternalIPv6 bool) (Source, error) {
tmpl, err := fqdn.ParseTemplate(fqdnTemplate) tmpl, err := fqdn.ParseTemplate(fqdnTemplate)
if err != nil { if err != nil {
return nil, err return nil, err
@ -141,6 +142,7 @@ func NewServiceSource(ctx context.Context, kubeClient kubernetes.Interface, name
labelSelector: labelSelector, labelSelector: labelSelector,
resolveLoadBalancerHostname: resolveLoadBalancerHostname, resolveLoadBalancerHostname: resolveLoadBalancerHostname,
listenEndpointEvents: listenEndpointEvents, listenEndpointEvents: listenEndpointEvents,
exposeInternalIPv6: exposeInternalIPv6,
}, nil }, nil
} }
@ -319,7 +321,7 @@ func (sc *serviceSource) extractHeadlessEndpoints(svc *v1.Service, hostname stri
return endpoints return endpoints
} }
for _, address := range node.Status.Addresses { for _, address := range node.Status.Addresses {
if address.Type == v1.NodeExternalIP || (address.Type == v1.NodeInternalIP && suitableType(address.Address) == endpoint.RecordTypeAAAA) { if address.Type == v1.NodeExternalIP || (sc.exposeInternalIPv6 && address.Type == v1.NodeInternalIP && suitableType(address.Address) == endpoint.RecordTypeAAAA) {
targets = append(targets, address.Address) targets = append(targets, address.Address)
log.Debugf("Generating matching endpoint %s with NodeExternalIP %s", headlessDomain, address.Address) log.Debugf("Generating matching endpoint %s with NodeExternalIP %s", headlessDomain, address.Address)
} }

View File

@ -82,6 +82,7 @@ func (suite *ServiceSuite) SetupTest() {
labels.Everything(), labels.Everything(),
false, false,
false, false,
false,
) )
suite.NoError(err, "should initialize service source") suite.NoError(err, "should initialize service source")
} }
@ -164,6 +165,7 @@ func testServiceSourceNewServiceSource(t *testing.T) {
labels.Everything(), labels.Everything(),
false, false,
false, false,
false,
) )
if ti.expectError { if ti.expectError {
@ -1136,6 +1138,7 @@ func testServiceSourceEndpoints(t *testing.T) {
sourceLabel, sourceLabel,
tc.resolveLoadBalancerHostname, tc.resolveLoadBalancerHostname,
false, false,
false,
) )
require.NoError(t, err) require.NoError(t, err)
@ -1351,6 +1354,7 @@ func testMultipleServicesEndpoints(t *testing.T) {
labels.Everything(), labels.Everything(),
false, false,
false, false,
false,
) )
require.NoError(t, err) require.NoError(t, err)
@ -1655,6 +1659,7 @@ func TestClusterIpServices(t *testing.T) {
labelSelector, labelSelector,
false, false,
false, false,
false,
) )
require.NoError(t, err) require.NoError(t, err)
@ -1686,6 +1691,7 @@ func TestServiceSourceNodePortServices(t *testing.T) {
compatibility string compatibility string
fqdnTemplate string fqdnTemplate string
ignoreHostnameAnnotation bool ignoreHostnameAnnotation bool
exposeInternalIPv6 bool
labels map[string]string labels map[string]string
annotations map[string]string annotations map[string]string
lbs []string lbs []string
@ -2207,12 +2213,13 @@ func TestServiceSourceNodePortServices(t *testing.T) {
}}, }},
}, },
{ {
title: "node port services annotated with external DNS Controller annotations return an endpoint in compatibility mode", title: "node port services annotated with external DNS Controller annotations return an endpoint in compatibility mode with exposeInternalIPv6 flag set",
svcNamespace: "testing", svcNamespace: "testing",
svcName: "foo", svcName: "foo",
svcType: v1.ServiceTypeNodePort, svcType: v1.ServiceTypeNodePort,
svcTrafficPolicy: v1.ServiceExternalTrafficPolicyTypeCluster, svcTrafficPolicy: v1.ServiceExternalTrafficPolicyTypeCluster,
compatibility: "kops-dns-controller", compatibility: "kops-dns-controller",
exposeInternalIPv6: true,
annotations: map[string]string{ annotations: map[string]string{
kopsDNSControllerHostnameAnnotationKey: "foo.example.org., bar.example.org", kopsDNSControllerHostnameAnnotationKey: "foo.example.org., bar.example.org",
}, },
@ -2374,6 +2381,7 @@ func TestServiceSourceNodePortServices(t *testing.T) {
labels.Everything(), labels.Everything(),
false, false,
false, false,
tc.exposeInternalIPv6,
) )
require.NoError(t, err) require.NoError(t, err)
@ -2403,6 +2411,7 @@ func TestHeadlessServices(t *testing.T) {
compatibility string compatibility string
fqdnTemplate string fqdnTemplate string
ignoreHostnameAnnotation bool ignoreHostnameAnnotation bool
exposeInternalIPv6 bool
labels map[string]string labels map[string]string
svcAnnotations map[string]string svcAnnotations map[string]string
podAnnotations map[string]string podAnnotations map[string]string
@ -2428,6 +2437,7 @@ func TestHeadlessServices(t *testing.T) {
"", "",
"", "",
false, false,
false,
map[string]string{"component": "foo"}, map[string]string{"component": "foo"},
map[string]string{ map[string]string{
hostnameAnnotationKey: "service.example.org", hostnameAnnotationKey: "service.example.org",
@ -2461,6 +2471,7 @@ func TestHeadlessServices(t *testing.T) {
"", "",
"", "",
false, false,
false,
map[string]string{"component": "foo"}, map[string]string{"component": "foo"},
map[string]string{ map[string]string{
hostnameAnnotationKey: "service.example.org", hostnameAnnotationKey: "service.example.org",
@ -2494,6 +2505,7 @@ func TestHeadlessServices(t *testing.T) {
"", "",
"", "",
true, true,
false,
map[string]string{"component": "foo"}, map[string]string{"component": "foo"},
map[string]string{ map[string]string{
hostnameAnnotationKey: "service.example.org", hostnameAnnotationKey: "service.example.org",
@ -2523,6 +2535,7 @@ func TestHeadlessServices(t *testing.T) {
"", "",
"", "",
false, false,
false,
map[string]string{"component": "foo"}, map[string]string{"component": "foo"},
map[string]string{ map[string]string{
hostnameAnnotationKey: "service.example.org", hostnameAnnotationKey: "service.example.org",
@ -2557,6 +2570,7 @@ func TestHeadlessServices(t *testing.T) {
"", "",
"", "",
false, false,
false,
map[string]string{"component": "foo"}, map[string]string{"component": "foo"},
map[string]string{ map[string]string{
hostnameAnnotationKey: "service.example.org", hostnameAnnotationKey: "service.example.org",
@ -2591,6 +2605,7 @@ func TestHeadlessServices(t *testing.T) {
"", "",
"", "",
false, false,
false,
map[string]string{"component": "foo"}, map[string]string{"component": "foo"},
map[string]string{ map[string]string{
hostnameAnnotationKey: "service.example.org", hostnameAnnotationKey: "service.example.org",
@ -2623,6 +2638,7 @@ func TestHeadlessServices(t *testing.T) {
"", "",
"", "",
false, false,
false,
map[string]string{"component": "foo"}, map[string]string{"component": "foo"},
map[string]string{ map[string]string{
hostnameAnnotationKey: "service.example.org", hostnameAnnotationKey: "service.example.org",
@ -2656,6 +2672,7 @@ func TestHeadlessServices(t *testing.T) {
"", "",
"", "",
false, false,
false,
map[string]string{"component": "foo"}, map[string]string{"component": "foo"},
map[string]string{ map[string]string{
hostnameAnnotationKey: "service.example.org", hostnameAnnotationKey: "service.example.org",
@ -2687,6 +2704,7 @@ func TestHeadlessServices(t *testing.T) {
"", "",
"", "",
false, false,
false,
map[string]string{"component": "foo"}, map[string]string{"component": "foo"},
map[string]string{ map[string]string{
hostnameAnnotationKey: "service.example.org", hostnameAnnotationKey: "service.example.org",
@ -2718,6 +2736,7 @@ func TestHeadlessServices(t *testing.T) {
"", "",
"", "",
false, false,
false,
map[string]string{"component": "foo"}, map[string]string{"component": "foo"},
map[string]string{ map[string]string{
hostnameAnnotationKey: "service.example.org", hostnameAnnotationKey: "service.example.org",
@ -2749,6 +2768,7 @@ func TestHeadlessServices(t *testing.T) {
"", "",
"", "",
false, false,
false,
map[string]string{"component": "foo"}, map[string]string{"component": "foo"},
map[string]string{ map[string]string{
hostnameAnnotationKey: "service.example.org", hostnameAnnotationKey: "service.example.org",
@ -2782,6 +2802,7 @@ func TestHeadlessServices(t *testing.T) {
"", "",
"", "",
false, false,
false,
map[string]string{"component": "foo"}, map[string]string{"component": "foo"},
map[string]string{ map[string]string{
hostnameAnnotationKey: "service.example.org", hostnameAnnotationKey: "service.example.org",
@ -2815,6 +2836,7 @@ func TestHeadlessServices(t *testing.T) {
"", "",
"", "",
false, false,
false,
map[string]string{"component": "foo"}, map[string]string{"component": "foo"},
map[string]string{ map[string]string{
hostnameAnnotationKey: "service.example.org", hostnameAnnotationKey: "service.example.org",
@ -2850,7 +2872,7 @@ func TestHeadlessServices(t *testing.T) {
false, false,
}, },
{ {
"annotated Headless services return IPv6 targets from node external IP if endpoints-type annotation is set", "annotated Headless services return only external IPv6 targets from node IP if endpoints-type annotation is set and exposeInternalIPv6 flag is unset",
"", "",
"testing", "testing",
"foo", "foo",
@ -2858,6 +2880,55 @@ func TestHeadlessServices(t *testing.T) {
"", "",
"", "",
false, false,
false,
map[string]string{"component": "foo"},
map[string]string{
hostnameAnnotationKey: "service.example.org",
endpointsTypeAnnotationKey: EndpointsTypeNodeExternalIP,
},
map[string]string{},
v1.ClusterIPNone,
[]string{"2001:db8::1"},
[]string{""},
map[string]string{
"component": "foo",
},
[]string{},
[]string{"foo"},
[]string{"", "", ""},
[]bool{true, true, true},
false,
[]v1.Node{
{
Status: v1.NodeStatus{
Addresses: []v1.NodeAddress{
{
Type: v1.NodeInternalIP,
Address: "2001:db8::4",
},
{
Type: v1.NodeExternalIP,
Address: "2001:db8::5",
},
},
},
},
},
[]*endpoint.Endpoint{
{DNSName: "service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::5"}},
},
false,
},
{
"annotated Headless services return IPv6 targets from node external IP if endpoints-type annotation is set and exposeInternalIPv6 flag set",
"",
"testing",
"foo",
v1.ServiceTypeClusterIP,
"",
"",
false,
true,
map[string]string{"component": "foo"}, map[string]string{"component": "foo"},
map[string]string{ map[string]string{
hostnameAnnotationKey: "service.example.org", hostnameAnnotationKey: "service.example.org",
@ -2893,7 +2964,7 @@ func TestHeadlessServices(t *testing.T) {
false, false,
}, },
{ {
"annotated Headless services return dual-stack targets from node external IP if endpoints-type annotation is set", "annotated Headless services return dual-stack targets from node external IP if endpoints-type annotation is set and exposeInternalIPv6 flag set",
"", "",
"testing", "testing",
"foo", "foo",
@ -2901,6 +2972,7 @@ func TestHeadlessServices(t *testing.T) {
"", "",
"", "",
false, false,
true,
map[string]string{"component": "foo"}, map[string]string{"component": "foo"},
map[string]string{ map[string]string{
hostnameAnnotationKey: "service.example.org", hostnameAnnotationKey: "service.example.org",
@ -2949,6 +3021,7 @@ func TestHeadlessServices(t *testing.T) {
"", "",
"", "",
false, false,
false,
map[string]string{"component": "foo"}, map[string]string{"component": "foo"},
map[string]string{ map[string]string{
hostnameAnnotationKey: "service.example.org", hostnameAnnotationKey: "service.example.org",
@ -2981,6 +3054,7 @@ func TestHeadlessServices(t *testing.T) {
"", "",
"", "",
false, false,
false,
map[string]string{"component": "foo"}, map[string]string{"component": "foo"},
map[string]string{ map[string]string{
hostnameAnnotationKey: "service.example.org", hostnameAnnotationKey: "service.example.org",
@ -3103,6 +3177,7 @@ func TestHeadlessServices(t *testing.T) {
labels.Everything(), labels.Everything(),
false, false,
false, false,
tc.exposeInternalIPv6,
) )
require.NoError(t, err) require.NoError(t, err)
@ -3563,6 +3638,7 @@ func TestHeadlessServicesHostIP(t *testing.T) {
labels.Everything(), labels.Everything(),
false, false,
false, false,
false,
) )
require.NoError(t, err) require.NoError(t, err)
@ -3742,6 +3818,7 @@ func TestExternalServices(t *testing.T) {
labels.Everything(), labels.Everything(),
false, false,
false, false,
false,
) )
require.NoError(t, err) require.NoError(t, err)
@ -3798,6 +3875,7 @@ func BenchmarkServiceEndpoints(b *testing.B) {
labels.Everything(), labels.Everything(),
false, false,
false, false,
false,
) )
require.NoError(b, err) require.NoError(b, err)

View File

@ -273,7 +273,7 @@ func BuildWithConfig(ctx context.Context, source string, p ClientGenerator, cfg
if err != nil { if err != nil {
return nil, err return nil, err
} }
return NewServiceSource(ctx, client, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation, cfg.Compatibility, cfg.PublishInternal, cfg.PublishHostIP, cfg.AlwaysPublishNotReadyAddresses, cfg.ServiceTypeFilter, cfg.IgnoreHostnameAnnotation, cfg.LabelFilter, cfg.ResolveLoadBalancerHostname, cfg.ListenEndpointEvents) return NewServiceSource(ctx, client, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation, cfg.Compatibility, cfg.PublishInternal, cfg.PublishHostIP, cfg.AlwaysPublishNotReadyAddresses, cfg.ServiceTypeFilter, cfg.IgnoreHostnameAnnotation, cfg.LabelFilter, cfg.ResolveLoadBalancerHostname, cfg.ListenEndpointEvents, cfg.ExposeInternalIPv6)
case "ingress": case "ingress":
client, err := p.KubeClient() client, err := p.KubeClient()
if err != nil { if err != nil {