mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2026-05-04 14:21:33 +02:00
Support AAAA records from headless services
This commit is contained in:
parent
3a788d6a44
commit
41c705e471
@ -271,7 +271,7 @@ func (sc *serviceSource) extractHeadlessEndpoints(svc *v1.Service, hostname stri
|
||||
|
||||
endpointsType := getEndpointsTypeFromAnnotations(svc.Annotations)
|
||||
|
||||
targetsByHeadlessDomain := make(map[string]endpoint.Targets)
|
||||
targetsByHeadlessDomainAndType := make(map[endpointKey]endpoint.Targets)
|
||||
for _, subset := range endpointsObject.Subsets {
|
||||
addresses := subset.Addresses
|
||||
if svc.Spec.PublishNotReadyAddresses || sc.alwaysPublishNotReadyAddresses {
|
||||
@ -324,18 +324,29 @@ func (sc *serviceSource) extractHeadlessEndpoints(svc *v1.Service, hostname stri
|
||||
log.Debugf("Generating matching endpoint %s with EndpointAddress IP %s", headlessDomain, address.IP)
|
||||
}
|
||||
}
|
||||
targetsByHeadlessDomain[headlessDomain] = append(targetsByHeadlessDomain[headlessDomain], targets...)
|
||||
for _, target := range targets {
|
||||
key := endpointKey{
|
||||
dnsName: headlessDomain,
|
||||
recordType: suitableType(target),
|
||||
}
|
||||
targetsByHeadlessDomainAndType[key] = append(targetsByHeadlessDomainAndType[key], target)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
headlessDomains := []string{}
|
||||
for headlessDomain := range targetsByHeadlessDomain {
|
||||
headlessDomains = append(headlessDomains, headlessDomain)
|
||||
headlessKeys := []endpointKey{}
|
||||
for headlessKey := range targetsByHeadlessDomainAndType {
|
||||
headlessKeys = append(headlessKeys, headlessKey)
|
||||
}
|
||||
sort.Strings(headlessDomains)
|
||||
for _, headlessDomain := range headlessDomains {
|
||||
allTargets := targetsByHeadlessDomain[headlessDomain]
|
||||
sort.Slice(headlessKeys, func(i, j int) bool {
|
||||
if headlessKeys[i].dnsName != headlessKeys[j].dnsName {
|
||||
return headlessKeys[i].dnsName < headlessKeys[j].dnsName
|
||||
}
|
||||
return headlessKeys[i].recordType < headlessKeys[j].recordType
|
||||
})
|
||||
for _, headlessKey := range headlessKeys {
|
||||
allTargets := targetsByHeadlessDomainAndType[headlessKey]
|
||||
targets := []string{}
|
||||
|
||||
deduppedTargets := map[string]struct{}{}
|
||||
@ -350,9 +361,9 @@ func (sc *serviceSource) extractHeadlessEndpoints(svc *v1.Service, hostname stri
|
||||
}
|
||||
|
||||
if ttl.IsConfigured() {
|
||||
endpoints = append(endpoints, endpoint.NewEndpointWithTTL(headlessDomain, endpoint.RecordTypeA, ttl, targets...))
|
||||
endpoints = append(endpoints, endpoint.NewEndpointWithTTL(headlessKey.dnsName, headlessKey.recordType, ttl, targets...))
|
||||
} else {
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint(headlessDomain, endpoint.RecordTypeA, targets...))
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint(headlessKey.dnsName, headlessKey.recordType, targets...))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2118,7 +2118,7 @@ func TestHeadlessServices(t *testing.T) {
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
"annotated Headless services return endpoints for each selected Pod",
|
||||
"annotated Headless services return IPv4 endpoints for each selected Pod",
|
||||
"",
|
||||
"testing",
|
||||
"foo",
|
||||
@ -2150,6 +2150,39 @@ func TestHeadlessServices(t *testing.T) {
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"annotated Headless services return IPv6 endpoints for each selected Pod",
|
||||
"",
|
||||
"testing",
|
||||
"foo",
|
||||
v1.ServiceTypeClusterIP,
|
||||
"",
|
||||
"",
|
||||
false,
|
||||
map[string]string{"component": "foo"},
|
||||
map[string]string{
|
||||
hostnameAnnotationKey: "service.example.org",
|
||||
},
|
||||
map[string]string{},
|
||||
v1.ClusterIPNone,
|
||||
[]string{"2001:db8::1", "2001:db8::2"},
|
||||
[]string{"", ""},
|
||||
map[string]string{
|
||||
"component": "foo",
|
||||
},
|
||||
[]string{},
|
||||
[]string{"foo-0", "foo-1"},
|
||||
[]string{"foo-0", "foo-1"},
|
||||
[]bool{true, true},
|
||||
false,
|
||||
[]v1.Node{},
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "foo-0.service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::1"}},
|
||||
{DNSName: "foo-1.service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::2"}},
|
||||
{DNSName: "service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::1", "2001:db8::2"}},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"hostname annotated Headless services are ignored",
|
||||
"",
|
||||
@ -2180,7 +2213,7 @@ func TestHeadlessServices(t *testing.T) {
|
||||
false,
|
||||
},
|
||||
{
|
||||
"annotated Headless services return endpoints with TTL for each selected Pod",
|
||||
"annotated Headless services return IPv4 endpoints with TTL for each selected Pod",
|
||||
"",
|
||||
"testing",
|
||||
"foo",
|
||||
@ -2213,6 +2246,40 @@ func TestHeadlessServices(t *testing.T) {
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"annotated Headless services return IPv6 endpoints with TTL for each selected Pod",
|
||||
"",
|
||||
"testing",
|
||||
"foo",
|
||||
v1.ServiceTypeClusterIP,
|
||||
"",
|
||||
"",
|
||||
false,
|
||||
map[string]string{"component": "foo"},
|
||||
map[string]string{
|
||||
hostnameAnnotationKey: "service.example.org",
|
||||
ttlAnnotationKey: "1",
|
||||
},
|
||||
map[string]string{},
|
||||
v1.ClusterIPNone,
|
||||
[]string{"2001:db8::1", "2001:db8::2"},
|
||||
[]string{"", ""},
|
||||
map[string]string{
|
||||
"component": "foo",
|
||||
},
|
||||
[]string{},
|
||||
[]string{"foo-0", "foo-1"},
|
||||
[]string{"foo-0", "foo-1"},
|
||||
[]bool{true, true},
|
||||
false,
|
||||
[]v1.Node{},
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "foo-0.service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::1"}, RecordTTL: endpoint.TTL(1)},
|
||||
{DNSName: "foo-1.service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::2"}, RecordTTL: endpoint.TTL(1)},
|
||||
{DNSName: "service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::1", "2001:db8::2"}, RecordTTL: endpoint.TTL(1)},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"annotated Headless services return endpoints for each selected Pod, which are in running state",
|
||||
"",
|
||||
@ -2310,7 +2377,7 @@ func TestHeadlessServices(t *testing.T) {
|
||||
false,
|
||||
},
|
||||
{
|
||||
"annotated Headless services return only a unique set of targets",
|
||||
"annotated Headless services return only a unique set of IPv4 targets",
|
||||
"",
|
||||
"testing",
|
||||
"foo",
|
||||
@ -2341,7 +2408,38 @@ func TestHeadlessServices(t *testing.T) {
|
||||
false,
|
||||
},
|
||||
{
|
||||
"annotated Headless services return targets from pod annotation",
|
||||
"annotated Headless services return only a unique set of IPv6 targets",
|
||||
"",
|
||||
"testing",
|
||||
"foo",
|
||||
v1.ServiceTypeClusterIP,
|
||||
"",
|
||||
"",
|
||||
false,
|
||||
map[string]string{"component": "foo"},
|
||||
map[string]string{
|
||||
hostnameAnnotationKey: "service.example.org",
|
||||
},
|
||||
map[string]string{},
|
||||
v1.ClusterIPNone,
|
||||
[]string{"2001:db8::1", "2001:db8::1", "2001:db8::2"},
|
||||
[]string{"", "", ""},
|
||||
map[string]string{
|
||||
"component": "foo",
|
||||
},
|
||||
[]string{},
|
||||
[]string{"foo-0", "foo-1", "foo-3"},
|
||||
[]string{"", "", ""},
|
||||
[]bool{true, true, true},
|
||||
false,
|
||||
[]v1.Node{},
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::1", "2001:db8::2"}},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"annotated Headless services return IPv4 targets from pod annotation",
|
||||
"",
|
||||
"testing",
|
||||
"foo",
|
||||
@ -2374,7 +2472,40 @@ func TestHeadlessServices(t *testing.T) {
|
||||
false,
|
||||
},
|
||||
{
|
||||
"annotated Headless services return targets from node external IP if endpoints-type annotation is set",
|
||||
"annotated Headless services return IPv6 targets from pod annotation",
|
||||
"",
|
||||
"testing",
|
||||
"foo",
|
||||
v1.ServiceTypeClusterIP,
|
||||
"",
|
||||
"",
|
||||
false,
|
||||
map[string]string{"component": "foo"},
|
||||
map[string]string{
|
||||
hostnameAnnotationKey: "service.example.org",
|
||||
},
|
||||
map[string]string{
|
||||
targetAnnotationKey: "2001:db8::4",
|
||||
},
|
||||
v1.ClusterIPNone,
|
||||
[]string{"2001:db8::1"},
|
||||
[]string{""},
|
||||
map[string]string{
|
||||
"component": "foo",
|
||||
},
|
||||
[]string{},
|
||||
[]string{"foo"},
|
||||
[]string{"", "", ""},
|
||||
[]bool{true, true, true},
|
||||
false,
|
||||
[]v1.Node{},
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::4"}},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"annotated Headless services return IPv4 targets from node external IP if endpoints-type annotation is set",
|
||||
"",
|
||||
"testing",
|
||||
"foo",
|
||||
@ -2417,7 +2548,98 @@ func TestHeadlessServices(t *testing.T) {
|
||||
false,
|
||||
},
|
||||
{
|
||||
"annotated Headless services return targets from hostIP if endpoints-type annotation is set",
|
||||
"annotated Headless services return IPv6 targets from node external IP if endpoints-type annotation is set",
|
||||
"",
|
||||
"testing",
|
||||
"foo",
|
||||
v1.ServiceTypeClusterIP,
|
||||
"",
|
||||
"",
|
||||
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",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::4"}},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"annotated Headless services return dual-stack targets from node external IP if endpoints-type annotation is set",
|
||||
"",
|
||||
"testing",
|
||||
"foo",
|
||||
v1.ServiceTypeClusterIP,
|
||||
"",
|
||||
"",
|
||||
false,
|
||||
map[string]string{"component": "foo"},
|
||||
map[string]string{
|
||||
hostnameAnnotationKey: "service.example.org",
|
||||
endpointsTypeAnnotationKey: EndpointsTypeNodeExternalIP,
|
||||
},
|
||||
map[string]string{},
|
||||
v1.ClusterIPNone,
|
||||
[]string{"1.1.1.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.NodeExternalIP,
|
||||
Address: "1.2.3.4",
|
||||
},
|
||||
{
|
||||
Type: v1.NodeInternalIP,
|
||||
Address: "2001:db8::4",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}},
|
||||
{DNSName: "service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::4"}},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"annotated Headless services return IPv4 targets from hostIP if endpoints-type annotation is set",
|
||||
"",
|
||||
"testing",
|
||||
"foo",
|
||||
@ -2448,6 +2670,38 @@ func TestHeadlessServices(t *testing.T) {
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"annotated Headless services return IPv6 targets from hostIP if endpoints-type annotation is set",
|
||||
"",
|
||||
"testing",
|
||||
"foo",
|
||||
v1.ServiceTypeClusterIP,
|
||||
"",
|
||||
"",
|
||||
false,
|
||||
map[string]string{"component": "foo"},
|
||||
map[string]string{
|
||||
hostnameAnnotationKey: "service.example.org",
|
||||
endpointsTypeAnnotationKey: EndpointsTypeHostIP,
|
||||
},
|
||||
map[string]string{},
|
||||
v1.ClusterIPNone,
|
||||
[]string{"2001:db8::1"},
|
||||
[]string{"2001:db8::4"},
|
||||
map[string]string{
|
||||
"component": "foo",
|
||||
},
|
||||
[]string{},
|
||||
[]string{"foo"},
|
||||
[]string{"", "", ""},
|
||||
[]bool{true, true, true},
|
||||
false,
|
||||
[]v1.Node{},
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::4"}},
|
||||
},
|
||||
false,
|
||||
},
|
||||
} {
|
||||
tc := tc
|
||||
t.Run(tc.title, func(t *testing.T) {
|
||||
@ -2590,7 +2844,7 @@ func TestHeadlessServicesHostIP(t *testing.T) {
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
"annotated Headless services return endpoints for each selected Pod",
|
||||
"annotated Headless services return IPv4 endpoints for each selected Pod",
|
||||
"",
|
||||
"testing",
|
||||
"foo",
|
||||
@ -2623,6 +2877,40 @@ func TestHeadlessServicesHostIP(t *testing.T) {
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"annotated Headless services return IPv6 endpoints for each selected Pod",
|
||||
"",
|
||||
"testing",
|
||||
"foo",
|
||||
v1.ServiceTypeClusterIP,
|
||||
"",
|
||||
"",
|
||||
false,
|
||||
map[string]string{"component": "foo"},
|
||||
map[string]string{
|
||||
hostnameAnnotationKey: "service.example.org",
|
||||
},
|
||||
v1.ClusterIPNone,
|
||||
[]string{"2001:db8::1", "2001:db8::2"},
|
||||
map[string]string{
|
||||
"component": "foo",
|
||||
},
|
||||
[]string{},
|
||||
[]string{"foo-0", "foo-1"},
|
||||
[]string{"foo-0", "foo-1"},
|
||||
[]bool{true, true},
|
||||
[]*v1.ObjectReference{
|
||||
{APIVersion: "", Kind: "Pod", Name: "foo-0"},
|
||||
{APIVersion: "", Kind: "Pod", Name: "foo-1"},
|
||||
},
|
||||
false,
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "foo-0.service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::1"}},
|
||||
{DNSName: "foo-1.service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::2"}},
|
||||
{DNSName: "service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::1", "2001:db8::2"}},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"hostname annotated Headless services are ignored",
|
||||
"",
|
||||
@ -2654,7 +2942,7 @@ func TestHeadlessServicesHostIP(t *testing.T) {
|
||||
false,
|
||||
},
|
||||
{
|
||||
"annotated Headless services return endpoints with TTL for each selected Pod",
|
||||
"annotated Headless services return IPv4 endpoints with TTL for each selected Pod",
|
||||
"",
|
||||
"testing",
|
||||
"foo",
|
||||
@ -2688,6 +2976,41 @@ func TestHeadlessServicesHostIP(t *testing.T) {
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"annotated Headless services return IPv6 endpoints with TTL for each selected Pod",
|
||||
"",
|
||||
"testing",
|
||||
"foo",
|
||||
v1.ServiceTypeClusterIP,
|
||||
"",
|
||||
"",
|
||||
false,
|
||||
map[string]string{"component": "foo"},
|
||||
map[string]string{
|
||||
hostnameAnnotationKey: "service.example.org",
|
||||
ttlAnnotationKey: "1",
|
||||
},
|
||||
v1.ClusterIPNone,
|
||||
[]string{"2001:db8::1", "2001:db8::2"},
|
||||
map[string]string{
|
||||
"component": "foo",
|
||||
},
|
||||
[]string{},
|
||||
[]string{"foo-0", "foo-1"},
|
||||
[]string{"foo-0", "foo-1"},
|
||||
[]bool{true, true},
|
||||
[]*v1.ObjectReference{
|
||||
{APIVersion: "", Kind: "Pod", Name: "foo-0"},
|
||||
{APIVersion: "", Kind: "Pod", Name: "foo-1"},
|
||||
},
|
||||
false,
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "foo-0.service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::1"}, RecordTTL: endpoint.TTL(1)},
|
||||
{DNSName: "foo-1.service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::2"}, RecordTTL: endpoint.TTL(1)},
|
||||
{DNSName: "service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::1", "2001:db8::2"}, RecordTTL: endpoint.TTL(1)},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"annotated Headless services return endpoints for each selected Pod, which are in running state",
|
||||
"",
|
||||
@ -2756,7 +3079,7 @@ func TestHeadlessServicesHostIP(t *testing.T) {
|
||||
false,
|
||||
},
|
||||
{
|
||||
"annotated Headless services return endpoints for pods missing hostname",
|
||||
"annotated Headless services return IPv4 endpoints for pods missing hostname",
|
||||
"",
|
||||
"testing",
|
||||
"foo",
|
||||
@ -2787,6 +3110,38 @@ func TestHeadlessServicesHostIP(t *testing.T) {
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"annotated Headless services return IPv6 endpoints for pods missing hostname",
|
||||
"",
|
||||
"testing",
|
||||
"foo",
|
||||
v1.ServiceTypeClusterIP,
|
||||
"",
|
||||
"",
|
||||
false,
|
||||
map[string]string{"component": "foo"},
|
||||
map[string]string{
|
||||
hostnameAnnotationKey: "service.example.org",
|
||||
},
|
||||
v1.ClusterIPNone,
|
||||
[]string{"2001:db8::1", "2001:db8::2"},
|
||||
map[string]string{
|
||||
"component": "foo",
|
||||
},
|
||||
[]string{},
|
||||
[]string{"foo-0", "foo-1"},
|
||||
[]string{"", ""},
|
||||
[]bool{true, true},
|
||||
[]*v1.ObjectReference{
|
||||
{APIVersion: "", Kind: "Pod", Name: "foo-0"},
|
||||
{APIVersion: "", Kind: "Pod", Name: "foo-1"},
|
||||
},
|
||||
false,
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::1", "2001:db8::2"}},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"annotated Headless services without a targetRef has no endpoints",
|
||||
"",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user