From 44f319e6a058b5ac4f54ae139e229d337f77dd58 Mon Sep 17 00:00:00 2001 From: Toshikuni Fukaya Date: Wed, 25 Jul 2018 18:35:26 +0900 Subject: [PATCH] Support A record for multile IPs for a headless services. Non statefulset pods associating to a headless service have different IPs, but have a same hostname. In this case, external-dns registered only one A record due to attempting to register multiple A records for a same hostname for each IP. This patch now registers one A record having multiple IPs. --- source/service.go | 27 +++++++++++++++++---------- source/service_test.go | 38 ++++++++++++++++++-------------------- 2 files changed, 35 insertions(+), 30 deletions(-) diff --git a/source/service.go b/source/service.go index cba3ce685..8d7831d30 100644 --- a/source/service.go +++ b/source/service.go @@ -156,6 +156,7 @@ func (sc *serviceSource) extractHeadlessEndpoints(svc *v1.Service, hostname stri return endpoints } + targetsByHeadlessDomain := make(map[string][]string) for _, v := range pods.Items { headlessDomain := hostname if v.Spec.Hostname != "" { @@ -166,11 +167,7 @@ func (sc *serviceSource) extractHeadlessEndpoints(svc *v1.Service, hostname stri log.Debugf("Generating matching endpoint %s with HostIP %s", headlessDomain, v.Status.HostIP) // To reduce traffice on the DNS API only add record for running Pods. Good Idea? if v.Status.Phase == v1.PodRunning { - if ttl.IsConfigured() { - endpoints = append(endpoints, endpoint.NewEndpointWithTTL(headlessDomain, endpoint.RecordTypeA, ttl, v.Status.HostIP)) - } else { - endpoints = append(endpoints, endpoint.NewEndpoint(headlessDomain, endpoint.RecordTypeA, v.Status.HostIP)) - } + targetsByHeadlessDomain[headlessDomain] = append(targetsByHeadlessDomain[headlessDomain], v.Status.HostIP) } else { log.Debugf("Pod %s is not in running phase", v.Spec.Hostname) } @@ -178,11 +175,7 @@ func (sc *serviceSource) extractHeadlessEndpoints(svc *v1.Service, hostname stri log.Debugf("Generating matching endpoint %s with PodIP %s", headlessDomain, v.Status.PodIP) // To reduce traffice on the DNS API only add record for running Pods. Good Idea? if v.Status.Phase == v1.PodRunning { - if ttl.IsConfigured() { - endpoints = append(endpoints, endpoint.NewEndpointWithTTL(headlessDomain, endpoint.RecordTypeA, ttl, v.Status.PodIP)) - } else { - endpoints = append(endpoints, endpoint.NewEndpoint(headlessDomain, endpoint.RecordTypeA, v.Status.PodIP)) - } + targetsByHeadlessDomain[headlessDomain] = append(targetsByHeadlessDomain[headlessDomain], v.Status.PodIP) } else { log.Debugf("Pod %s is not in running phase", v.Spec.Hostname) } @@ -190,6 +183,20 @@ func (sc *serviceSource) extractHeadlessEndpoints(svc *v1.Service, hostname stri } + headlessDomains := []string{} + for headlessDomain := range targetsByHeadlessDomain { + headlessDomains = append(headlessDomains, headlessDomain) + } + sort.Strings(headlessDomains) + for _, headlessDomain := range headlessDomains { + targets := targetsByHeadlessDomain[headlessDomain] + if ttl.IsConfigured() { + endpoints = append(endpoints, endpoint.NewEndpointWithTTL(headlessDomain, endpoint.RecordTypeA, ttl, targets...)) + } else { + endpoints = append(endpoints, endpoint.NewEndpoint(headlessDomain, endpoint.RecordTypeA, targets...)) + } + } + return endpoints } diff --git a/source/service_test.go b/source/service_test.go index 2e6a59b1f..9989cd1e7 100644 --- a/source/service_test.go +++ b/source/service_test.go @@ -1235,7 +1235,7 @@ func TestHeadlessServices(t *testing.T) { labels map[string]string annotations map[string]string clusterIP string - podIP string + podIPs []string selector map[string]string lbs []string podnames []string @@ -1257,7 +1257,7 @@ func TestHeadlessServices(t *testing.T) { hostnameAnnotationKey: "service.example.org", }, v1.ClusterIPNone, - "1.1.1.1", + []string{"1.1.1.1", "1.1.1.2"}, map[string]string{ "component": "foo", }, @@ -1267,7 +1267,7 @@ func TestHeadlessServices(t *testing.T) { []v1.PodPhase{v1.PodRunning, v1.PodRunning}, []*endpoint.Endpoint{ {DNSName: "foo-0.service.example.org", Targets: endpoint.Targets{"1.1.1.1"}}, - {DNSName: "foo-1.service.example.org", Targets: endpoint.Targets{"1.1.1.1"}}, + {DNSName: "foo-1.service.example.org", Targets: endpoint.Targets{"1.1.1.2"}}, }, false, }, @@ -1285,7 +1285,7 @@ func TestHeadlessServices(t *testing.T) { ttlAnnotationKey: "1", }, v1.ClusterIPNone, - "1.1.1.1", + []string{"1.1.1.1", "1.1.1.2"}, map[string]string{ "component": "foo", }, @@ -1295,7 +1295,7 @@ func TestHeadlessServices(t *testing.T) { []v1.PodPhase{v1.PodRunning, v1.PodRunning}, []*endpoint.Endpoint{ {DNSName: "foo-0.service.example.org", Targets: endpoint.Targets{"1.1.1.1"}, RecordTTL: endpoint.TTL(1)}, - {DNSName: "foo-1.service.example.org", Targets: endpoint.Targets{"1.1.1.1"}, RecordTTL: endpoint.TTL(1)}, + {DNSName: "foo-1.service.example.org", Targets: endpoint.Targets{"1.1.1.2"}, RecordTTL: endpoint.TTL(1)}, }, false, }, @@ -1312,7 +1312,7 @@ func TestHeadlessServices(t *testing.T) { hostnameAnnotationKey: "service.example.org", }, v1.ClusterIPNone, - "1.1.1.1", + []string{"1.1.1.1", "1.1.1.2"}, map[string]string{ "component": "foo", }, @@ -1338,7 +1338,7 @@ func TestHeadlessServices(t *testing.T) { hostnameAnnotationKey: "service.example.org", }, v1.ClusterIPNone, - "1.1.1.1", + []string{"1.1.1.1", "1.1.1.2"}, map[string]string{ "component": "foo", }, @@ -1347,8 +1347,7 @@ func TestHeadlessServices(t *testing.T) { []string{"", ""}, []v1.PodPhase{v1.PodRunning, v1.PodRunning}, []*endpoint.Endpoint{ - {DNSName: "service.example.org", Targets: endpoint.Targets{"1.1.1.1"}}, - {DNSName: "service.example.org", Targets: endpoint.Targets{"1.1.1.1"}}, + {DNSName: "service.example.org", Targets: endpoint.Targets{"1.1.1.1", "1.1.1.2"}}, }, false, }, @@ -1387,7 +1386,7 @@ func TestHeadlessServices(t *testing.T) { Annotations: tc.annotations, }, Status: v1.PodStatus{ - PodIP: tc.podIP, + PodIP: tc.podIPs[i], Phase: tc.phases[i], }, } @@ -1435,7 +1434,7 @@ func TestHeadlessServicesHostIP(t *testing.T) { labels map[string]string annotations map[string]string clusterIP string - hostIP string + hostIPs []string selector map[string]string lbs []string podnames []string @@ -1457,7 +1456,7 @@ func TestHeadlessServicesHostIP(t *testing.T) { hostnameAnnotationKey: "service.example.org", }, v1.ClusterIPNone, - "1.1.1.1", + []string{"1.1.1.1", "1.1.1.2"}, map[string]string{ "component": "foo", }, @@ -1467,7 +1466,7 @@ func TestHeadlessServicesHostIP(t *testing.T) { []v1.PodPhase{v1.PodRunning, v1.PodRunning}, []*endpoint.Endpoint{ {DNSName: "foo-0.service.example.org", Targets: endpoint.Targets{"1.1.1.1"}}, - {DNSName: "foo-1.service.example.org", Targets: endpoint.Targets{"1.1.1.1"}}, + {DNSName: "foo-1.service.example.org", Targets: endpoint.Targets{"1.1.1.2"}}, }, false, }, @@ -1485,7 +1484,7 @@ func TestHeadlessServicesHostIP(t *testing.T) { ttlAnnotationKey: "1", }, v1.ClusterIPNone, - "1.1.1.1", + []string{"1.1.1.1", "1.1.1.2"}, map[string]string{ "component": "foo", }, @@ -1495,7 +1494,7 @@ func TestHeadlessServicesHostIP(t *testing.T) { []v1.PodPhase{v1.PodRunning, v1.PodRunning}, []*endpoint.Endpoint{ {DNSName: "foo-0.service.example.org", Targets: endpoint.Targets{"1.1.1.1"}, RecordTTL: endpoint.TTL(1)}, - {DNSName: "foo-1.service.example.org", Targets: endpoint.Targets{"1.1.1.1"}, RecordTTL: endpoint.TTL(1)}, + {DNSName: "foo-1.service.example.org", Targets: endpoint.Targets{"1.1.1.2"}, RecordTTL: endpoint.TTL(1)}, }, false, }, @@ -1512,7 +1511,7 @@ func TestHeadlessServicesHostIP(t *testing.T) { hostnameAnnotationKey: "service.example.org", }, v1.ClusterIPNone, - "1.1.1.1", + []string{"1.1.1.1", "1.1.1.2"}, map[string]string{ "component": "foo", }, @@ -1538,7 +1537,7 @@ func TestHeadlessServicesHostIP(t *testing.T) { hostnameAnnotationKey: "service.example.org", }, v1.ClusterIPNone, - "1.1.1.1", + []string{"1.1.1.1", "1.1.1.2"}, map[string]string{ "component": "foo", }, @@ -1547,8 +1546,7 @@ func TestHeadlessServicesHostIP(t *testing.T) { []string{"", ""}, []v1.PodPhase{v1.PodRunning, v1.PodRunning}, []*endpoint.Endpoint{ - {DNSName: "service.example.org", Targets: endpoint.Targets{"1.1.1.1"}}, - {DNSName: "service.example.org", Targets: endpoint.Targets{"1.1.1.1"}}, + {DNSName: "service.example.org", Targets: endpoint.Targets{"1.1.1.1", "1.1.1.2"}}, }, false, }, @@ -1587,7 +1585,7 @@ func TestHeadlessServicesHostIP(t *testing.T) { Annotations: tc.annotations, }, Status: v1.PodStatus{ - HostIP: tc.hostIP, + HostIP: tc.hostIPs[i], Phase: tc.phases[i], }, }