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: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},
{"::1", true},
{"::", true},

View File

@ -159,7 +159,7 @@ func legacyEndpointsFromDNSControllerNodePortService(svc *v1.Service, sc *servic
for _, address := range node.Status.Addresses {
recordType := suitableType(address.Address)
// 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))
}
if isInternal && address.Type == v1.NodeInternalIP {

View File

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

View File

@ -65,10 +65,11 @@ type serviceSource struct {
nodeInformer coreinformers.NodeInformer
serviceTypeFilter map[string]struct{}
labelSelector labels.Selector
exposeInternalIPv6 bool
}
// 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)
if err != nil {
return nil, err
@ -141,6 +142,7 @@ func NewServiceSource(ctx context.Context, kubeClient kubernetes.Interface, name
labelSelector: labelSelector,
resolveLoadBalancerHostname: resolveLoadBalancerHostname,
listenEndpointEvents: listenEndpointEvents,
exposeInternalIPv6: exposeInternalIPv6,
}, nil
}
@ -319,7 +321,7 @@ func (sc *serviceSource) extractHeadlessEndpoints(svc *v1.Service, hostname stri
return endpoints
}
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)
log.Debugf("Generating matching endpoint %s with NodeExternalIP %s", headlessDomain, address.Address)
}

View File

@ -82,6 +82,7 @@ func (suite *ServiceSuite) SetupTest() {
labels.Everything(),
false,
false,
false,
)
suite.NoError(err, "should initialize service source")
}
@ -164,6 +165,7 @@ func testServiceSourceNewServiceSource(t *testing.T) {
labels.Everything(),
false,
false,
false,
)
if ti.expectError {
@ -1136,6 +1138,7 @@ func testServiceSourceEndpoints(t *testing.T) {
sourceLabel,
tc.resolveLoadBalancerHostname,
false,
false,
)
require.NoError(t, err)
@ -1351,6 +1354,7 @@ func testMultipleServicesEndpoints(t *testing.T) {
labels.Everything(),
false,
false,
false,
)
require.NoError(t, err)
@ -1655,6 +1659,7 @@ func TestClusterIpServices(t *testing.T) {
labelSelector,
false,
false,
false,
)
require.NoError(t, err)
@ -1686,6 +1691,7 @@ func TestServiceSourceNodePortServices(t *testing.T) {
compatibility string
fqdnTemplate string
ignoreHostnameAnnotation bool
exposeInternalIPv6 bool
labels map[string]string
annotations map[string]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",
svcName: "foo",
svcType: v1.ServiceTypeNodePort,
svcTrafficPolicy: v1.ServiceExternalTrafficPolicyTypeCluster,
compatibility: "kops-dns-controller",
exposeInternalIPv6: true,
annotations: map[string]string{
kopsDNSControllerHostnameAnnotationKey: "foo.example.org., bar.example.org",
},
@ -2374,6 +2381,7 @@ func TestServiceSourceNodePortServices(t *testing.T) {
labels.Everything(),
false,
false,
tc.exposeInternalIPv6,
)
require.NoError(t, err)
@ -2403,6 +2411,7 @@ func TestHeadlessServices(t *testing.T) {
compatibility string
fqdnTemplate string
ignoreHostnameAnnotation bool
exposeInternalIPv6 bool
labels map[string]string
svcAnnotations map[string]string
podAnnotations map[string]string
@ -2428,6 +2437,7 @@ func TestHeadlessServices(t *testing.T) {
"",
"",
false,
false,
map[string]string{"component": "foo"},
map[string]string{
hostnameAnnotationKey: "service.example.org",
@ -2461,6 +2471,7 @@ func TestHeadlessServices(t *testing.T) {
"",
"",
false,
false,
map[string]string{"component": "foo"},
map[string]string{
hostnameAnnotationKey: "service.example.org",
@ -2494,6 +2505,7 @@ func TestHeadlessServices(t *testing.T) {
"",
"",
true,
false,
map[string]string{"component": "foo"},
map[string]string{
hostnameAnnotationKey: "service.example.org",
@ -2523,6 +2535,7 @@ func TestHeadlessServices(t *testing.T) {
"",
"",
false,
false,
map[string]string{"component": "foo"},
map[string]string{
hostnameAnnotationKey: "service.example.org",
@ -2557,6 +2570,7 @@ func TestHeadlessServices(t *testing.T) {
"",
"",
false,
false,
map[string]string{"component": "foo"},
map[string]string{
hostnameAnnotationKey: "service.example.org",
@ -2591,6 +2605,7 @@ func TestHeadlessServices(t *testing.T) {
"",
"",
false,
false,
map[string]string{"component": "foo"},
map[string]string{
hostnameAnnotationKey: "service.example.org",
@ -2623,6 +2638,7 @@ func TestHeadlessServices(t *testing.T) {
"",
"",
false,
false,
map[string]string{"component": "foo"},
map[string]string{
hostnameAnnotationKey: "service.example.org",
@ -2656,6 +2672,7 @@ func TestHeadlessServices(t *testing.T) {
"",
"",
false,
false,
map[string]string{"component": "foo"},
map[string]string{
hostnameAnnotationKey: "service.example.org",
@ -2687,6 +2704,7 @@ func TestHeadlessServices(t *testing.T) {
"",
"",
false,
false,
map[string]string{"component": "foo"},
map[string]string{
hostnameAnnotationKey: "service.example.org",
@ -2718,6 +2736,7 @@ func TestHeadlessServices(t *testing.T) {
"",
"",
false,
false,
map[string]string{"component": "foo"},
map[string]string{
hostnameAnnotationKey: "service.example.org",
@ -2749,6 +2768,7 @@ func TestHeadlessServices(t *testing.T) {
"",
"",
false,
false,
map[string]string{"component": "foo"},
map[string]string{
hostnameAnnotationKey: "service.example.org",
@ -2782,6 +2802,7 @@ func TestHeadlessServices(t *testing.T) {
"",
"",
false,
false,
map[string]string{"component": "foo"},
map[string]string{
hostnameAnnotationKey: "service.example.org",
@ -2815,6 +2836,7 @@ func TestHeadlessServices(t *testing.T) {
"",
"",
false,
false,
map[string]string{"component": "foo"},
map[string]string{
hostnameAnnotationKey: "service.example.org",
@ -2850,7 +2872,7 @@ func TestHeadlessServices(t *testing.T) {
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",
"foo",
@ -2858,6 +2880,55 @@ func TestHeadlessServices(t *testing.T) {
"",
"",
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{
hostnameAnnotationKey: "service.example.org",
@ -2893,7 +2964,7 @@ func TestHeadlessServices(t *testing.T) {
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",
"foo",
@ -2901,6 +2972,7 @@ func TestHeadlessServices(t *testing.T) {
"",
"",
false,
true,
map[string]string{"component": "foo"},
map[string]string{
hostnameAnnotationKey: "service.example.org",
@ -2949,6 +3021,7 @@ func TestHeadlessServices(t *testing.T) {
"",
"",
false,
false,
map[string]string{"component": "foo"},
map[string]string{
hostnameAnnotationKey: "service.example.org",
@ -2981,6 +3054,7 @@ func TestHeadlessServices(t *testing.T) {
"",
"",
false,
false,
map[string]string{"component": "foo"},
map[string]string{
hostnameAnnotationKey: "service.example.org",
@ -3103,6 +3177,7 @@ func TestHeadlessServices(t *testing.T) {
labels.Everything(),
false,
false,
tc.exposeInternalIPv6,
)
require.NoError(t, err)
@ -3563,6 +3638,7 @@ func TestHeadlessServicesHostIP(t *testing.T) {
labels.Everything(),
false,
false,
false,
)
require.NoError(t, err)
@ -3742,6 +3818,7 @@ func TestExternalServices(t *testing.T) {
labels.Everything(),
false,
false,
false,
)
require.NoError(t, err)
@ -3798,6 +3875,7 @@ func BenchmarkServiceEndpoints(b *testing.B) {
labels.Everything(),
false,
false,
false,
)
require.NoError(b, err)

View File

@ -273,7 +273,7 @@ func BuildWithConfig(ctx context.Context, source string, p ClientGenerator, cfg
if err != nil {
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":
client, err := p.KubeClient()
if err != nil {