diff --git a/endpoint/endpoint.go b/endpoint/endpoint.go index f4c9405da..6c22c4fba 100644 --- a/endpoint/endpoint.go +++ b/endpoint/endpoint.go @@ -166,7 +166,7 @@ type Endpoint struct { DNSName string `json:"dnsName,omitempty"` // The targets the DNS record points to Targets Targets `json:"targets,omitempty"` - // RecordType type of record, e.g. CNAME, A, SRV, TXT etc + // RecordType type of record, e.g. CNAME, A, AAAA, SRV, TXT etc RecordType string `json:"recordType,omitempty"` // Identifier to distinguish multiple records with the same name and type (e.g. Route53 records with routing policies other than 'simple') SetIdentifier string `json:"setIdentifier,omitempty"` diff --git a/endpoint/endpoint_test.go b/endpoint/endpoint_test.go index 81e7f4c33..fe61f8dc6 100644 --- a/endpoint/endpoint_test.go +++ b/endpoint/endpoint_test.go @@ -35,6 +35,22 @@ func TestNewEndpoint(t *testing.T) { } } +func TestNewEndpointWithIPv6(t *testing.T) { + e := NewEndpoint("example.org", "AAAA", "foo.com") + if e.DNSName != "example.com" || e.Targets[0] != "foo.com" || e.RecordType != "AAAA" { + t.Error("Endpoint is not initialized correctly") + } + + if e.Labels == nil { + t.Error("Labels is not initialized") + } + + w := NewEndpoint("example.org", "", "load-balancer.com.") + if w.DNSName != "example.org" || e.Targets[0] != "load-balancer.com" || w.RecordType != "" { + t.Error("Endpoint is not initialized correctly") + } +} + func TestTargetsSame(t *testing.T) { tests := []Targets{ {""}, diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index 1eb8570bf..e573669e7 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -327,7 +327,7 @@ var defaultConfig = &Config{ TransIPAccountName: "", TransIPPrivateKeyFile: "", DigitalOceanAPIPageSize: 50, - ManagedDNSRecordTypes: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME}, + ManagedDNSRecordTypes: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME}, GoDaddyAPIKey: "", GoDaddySecretKey: "", GoDaddyTTL: 600, @@ -424,7 +424,7 @@ func (cfg *Config) ParseFlags(args []string) error { app.Flag("crd-source-apiversion", "API version of the CRD for crd source, e.g. `externaldns.k8s.io/v1alpha1`, valid only when using crd source").Default(defaultConfig.CRDSourceAPIVersion).StringVar(&cfg.CRDSourceAPIVersion) app.Flag("crd-source-kind", "Kind of the CRD for the crd source in API group and version specified by crd-source-apiversion").Default(defaultConfig.CRDSourceKind).StringVar(&cfg.CRDSourceKind) app.Flag("service-type-filter", "The service types to take care about (default: all, expected: ClusterIP, NodePort, LoadBalancer or ExternalName)").StringsVar(&cfg.ServiceTypeFilter) - app.Flag("managed-record-types", "Record types to manage; specify multiple times to include many; (default: A, CNAME) (supported records: CNAME, A, NS").Default("A", "CNAME").StringsVar(&cfg.ManagedDNSRecordTypes) + app.Flag("managed-record-types", "Record types to manage; specify multiple times to include many; (default: A, AAAA, CNAME) (supported records: CNAME, A, AAAA, NS").Default("A", "AAAA", "CNAME").StringsVar(&cfg.ManagedDNSRecordTypes) app.Flag("default-targets", "Set globally default IP address that will apply as a target instead of source addresses. Specify multiple times for multiple targets (optional)").StringsVar(&cfg.DefaultTargets) app.Flag("target-net-filter", "Limit possible targets by a net filter; specify multiple times for multiple possible nets (optional)").StringsVar(&cfg.TargetNetFilter) app.Flag("exclude-target-net", "Exclude target nets (optional)").StringsVar(&cfg.ExcludeTargetNets) diff --git a/pkg/apis/externaldns/types_test.go b/pkg/apis/externaldns/types_test.go index 87647ee21..7d4cc6ab8 100644 --- a/pkg/apis/externaldns/types_test.go +++ b/pkg/apis/externaldns/types_test.go @@ -122,7 +122,7 @@ var ( TransIPAccountName: "", TransIPPrivateKeyFile: "", DigitalOceanAPIPageSize: 50, - ManagedDNSRecordTypes: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME}, + ManagedDNSRecordTypes: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME}, RFC2136BatchChangeSize: 50, OCPRouterName: "default", IBMCloudProxied: false, @@ -233,7 +233,7 @@ var ( TransIPAccountName: "transip", TransIPPrivateKeyFile: "/path/to/transip.key", DigitalOceanAPIPageSize: 100, - ManagedDNSRecordTypes: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME, endpoint.RecordTypeNS}, + ManagedDNSRecordTypes: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME, endpoint.RecordTypeNS}, RFC2136BatchChangeSize: 100, IBMCloudProxied: true, IBMCloudConfigFile: "ibmcloud.json", diff --git a/provider/pdns/pdns.go b/provider/pdns/pdns.go index 6f9ca0266..ff8611dc3 100644 --- a/provider/pdns/pdns.go +++ b/provider/pdns/pdns.go @@ -315,7 +315,6 @@ func (p *PDNSProvider) ConvertEndpointsToZones(eps []*endpoint.Endpoint, changet if ep.RecordType == "CNAME" { t = provider.EnsureTrailingDot(t) } - records = append(records, pgo.Record{Content: t}) } rrset := pgo.RrSet{ diff --git a/source/service.go b/source/service.go index 9c47579dd..aa8311f39 100644 --- a/source/service.go +++ b/source/service.go @@ -456,6 +456,14 @@ func (sc *serviceSource) generateEndpoints(svc *v1.Service, hostname string, pro DNSName: hostname, } + epAAAA := &endpoint.Endpoint{ + RecordTTL: ttl, + RecordType: endpoint.RecordTypeAAAA, + Labels: endpoint.NewLabels(), + Targets: make(endpoint.Targets, 0, defaultTargetsCapacity), + DNSName: hostname, + } + epCNAME := &endpoint.Endpoint{ RecordTTL: ttl, RecordType: endpoint.RecordTypeCNAME, @@ -497,6 +505,9 @@ func (sc *serviceSource) generateEndpoints(svc *v1.Service, hostname string, pro if suitableType(t) == endpoint.RecordTypeA { epA.Targets = append(epA.Targets, t) } + if suitableType(t) == endpoint.RecordTypeAAAA { + epAAAA.Targets = append(epAAAA.Targets, t) + } if suitableType(t) == endpoint.RecordTypeCNAME { epCNAME.Targets = append(epCNAME.Targets, t) } @@ -505,6 +516,9 @@ func (sc *serviceSource) generateEndpoints(svc *v1.Service, hostname string, pro if len(epA.Targets) > 0 { endpoints = append(endpoints, epA) } + if len(epAAAA.Targets) > 0 { + endpoints = append(endpoints, epAAAA) + } if len(epCNAME.Targets) > 0 { endpoints = append(endpoints, epCNAME) } diff --git a/source/service_test.go b/source/service_test.go index b0ce34c97..932d172e3 100644 --- a/source/service_test.go +++ b/source/service_test.go @@ -187,6 +187,7 @@ func testServiceSourceEndpoints(t *testing.T) { labels map[string]string annotations map[string]string clusterIP string + ipFamilies []v1.IPFamily externalIPs []string lbs []string serviceTypesFilter []string @@ -201,6 +202,7 @@ func testServiceSourceEndpoints(t *testing.T) { svcType: v1.ServiceTypeLoadBalancer, labels: map[string]string{}, annotations: map[string]string{}, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -214,6 +216,7 @@ func testServiceSourceEndpoints(t *testing.T) { ignoreHostnameAnnotation: true, labels: map[string]string{}, annotations: map[string]string{}, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -228,6 +231,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -245,6 +249,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -259,6 +264,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, clusterIP: "1.2.3.4", externalIPs: []string{}, lbs: []string{}, @@ -273,6 +279,7 @@ func testServiceSourceEndpoints(t *testing.T) { fqdnTemplate: "{{.Name}}.fqdn.org,{{.Name}}.fqdn.com", labels: map[string]string{}, annotations: map[string]string{}, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -290,6 +297,7 @@ func testServiceSourceEndpoints(t *testing.T) { ignoreHostnameAnnotation: true, labels: map[string]string{}, annotations: map[string]string{}, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -309,6 +317,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org., bar.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -331,6 +340,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org., bar.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -348,6 +358,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org., bar.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -365,6 +376,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org, bar.example.org", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -382,6 +394,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"lb.example.com"}, // Kubernetes omits the trailing dot serviceTypesFilter: []string{}, @@ -398,6 +411,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org", // Trailing dot is omitted }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4", "lb.example.com"}, // Kubernetes omits the trailing dot serviceTypesFilter: []string{}, @@ -416,6 +430,7 @@ func testServiceSourceEndpoints(t *testing.T) { controllerAnnotationKey: controllerAnnotationValue, hostnameAnnotationKey: "foo.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -434,6 +449,7 @@ func testServiceSourceEndpoints(t *testing.T) { controllerAnnotationKey: "some-other-tool", hostnameAnnotationKey: "foo.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -449,6 +465,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -466,6 +483,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -480,6 +498,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -498,6 +517,7 @@ func testServiceSourceEndpoints(t *testing.T) { hostnameAnnotationKey: "foo.example.org.", "service.beta.kubernetes.io/external-traffic": "OnlyLocal", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -516,6 +536,7 @@ func testServiceSourceEndpoints(t *testing.T) { hostnameAnnotationKey: "foo.example.org.", "service.beta.kubernetes.io/external-traffic": "SomethingElse", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -532,6 +553,7 @@ func testServiceSourceEndpoints(t *testing.T) { hostnameAnnotationKey: "foo.example.org.", "service.beta.kubernetes.io/external-traffic": "OnlyLocal", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -549,6 +571,7 @@ func testServiceSourceEndpoints(t *testing.T) { hostnameAnnotationKey: "foo.example.org.", "service.beta.kubernetes.io/external-traffic": "Global", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -567,6 +590,7 @@ func testServiceSourceEndpoints(t *testing.T) { hostnameAnnotationKey: "foo.example.org.", "service.beta.kubernetes.io/external-traffic": "OnlyLocal", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -581,6 +605,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{}, serviceTypesFilter: []string{}, @@ -595,6 +620,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{"10.2.3.4", "11.2.3.4"}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -611,6 +637,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4", "8.8.8.8"}, serviceTypesFilter: []string{}, @@ -627,6 +654,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ "zalando.org/dnsname": "foo.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -642,6 +670,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ "zalando.org/dnsname": "foo.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -658,6 +687,7 @@ func testServiceSourceEndpoints(t *testing.T) { labels: map[string]string{ "dns": "route53", }, + ipFamilies: []v1.IPFamily{"IPv4"}, annotations: map[string]string{ "domainName": "foo.example.org., bar.example.org", }, @@ -679,6 +709,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ kopsDNSControllerInternalHostnameAnnotationKey: "internal.foo.example.org", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4", "lb.example.com"}, serviceTypesFilter: []string{}, @@ -698,6 +729,7 @@ func testServiceSourceEndpoints(t *testing.T) { kopsDNSControllerInternalHostnameAnnotationKey: "internal.foo.example.org., internal.bar.example.org", kopsDNSControllerHostnameAnnotationKey: "foo.example.org., bar.example.org", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -716,6 +748,7 @@ func testServiceSourceEndpoints(t *testing.T) { fqdnTemplate: "{{.Name}}.bar.example.com", labels: map[string]string{}, annotations: map[string]string{}, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4", "elb.com"}, serviceTypesFilter: []string{}, @@ -734,6 +767,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4", "elb.com"}, serviceTypesFilter: []string{}, @@ -753,6 +787,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ "zalando.org/dnsname": "mate.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -768,6 +803,7 @@ func testServiceSourceEndpoints(t *testing.T) { fqdnTemplate: "{{.Calibre}}.bar.example.com", labels: map[string]string{}, annotations: map[string]string{}, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -783,6 +819,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -800,6 +837,7 @@ func testServiceSourceEndpoints(t *testing.T) { hostnameAnnotationKey: "foo.example.org.", ttlAnnotationKey: "foo", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -817,6 +855,7 @@ func testServiceSourceEndpoints(t *testing.T) { hostnameAnnotationKey: "foo.example.org.", ttlAnnotationKey: "10", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -834,6 +873,7 @@ func testServiceSourceEndpoints(t *testing.T) { hostnameAnnotationKey: "foo.example.org.", ttlAnnotationKey: "1m", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -851,6 +891,7 @@ func testServiceSourceEndpoints(t *testing.T) { hostnameAnnotationKey: "foo.example.org.", ttlAnnotationKey: "-10", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -867,6 +908,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{string(v1.ServiceTypeLoadBalancer)}, @@ -883,6 +925,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{string(v1.ServiceTypeLoadBalancer)}, @@ -897,6 +940,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ internalHostnameAnnotationKey: "foo.internal.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, clusterIP: "1.1.1.1", externalIPs: []string{}, lbs: []string{"1.2.3.4"}, @@ -915,6 +959,7 @@ func testServiceSourceEndpoints(t *testing.T) { hostnameAnnotationKey: "foo.example.org.", internalHostnameAnnotationKey: "foo.internal.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, clusterIP: "1.1.1.1", externalIPs: []string{}, lbs: []string{"1.2.3.4"}, @@ -932,6 +977,7 @@ func testServiceSourceEndpoints(t *testing.T) { labels: map[string]string{ "app": "web-external", }, + ipFamilies: []v1.IPFamily{"IPv4"}, clusterIP: "1.1.1.1", externalIPs: []string{}, lbs: []string{"1.2.3.4"}, @@ -950,6 +996,7 @@ func testServiceSourceEndpoints(t *testing.T) { labels: map[string]string{ "app": "web-external", }, + ipFamilies: []v1.IPFamily{"IPv4"}, clusterIP: "1.1.1.1", externalIPs: []string{}, lbs: []string{"1.2.3.4"}, @@ -968,6 +1015,7 @@ func testServiceSourceEndpoints(t *testing.T) { labels: map[string]string{ "app": "web-internal", }, + ipFamilies: []v1.IPFamily{"IPv4"}, clusterIP: "1.1.1.1", externalIPs: []string{}, lbs: []string{"1.2.3.4"}, @@ -984,6 +1032,7 @@ func testServiceSourceEndpoints(t *testing.T) { labels: map[string]string{ "app": "web-internal", }, + ipFamilies: []v1.IPFamily{"IPv4"}, clusterIP: "1.1.1.1", externalIPs: []string{}, lbs: []string{"1.2.3.4"}, @@ -992,6 +1041,38 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{hostnameAnnotationKey: "annotation.bar.example.com"}, expected: []*endpoint.Endpoint{}, }, + { + title: "dual-stack load-balancer service gets both addresses", + svcNamespace: "testing", + svcName: "foobar", + svcType: v1.ServiceTypeLoadBalancer, + labels: map[string]string{}, + ipFamilies: []v1.IPFamily{"IPv4", "IPv6"}, + clusterIP: "1.1.1.2,2001:db8::2", + externalIPs: []string{}, + lbs: []string{"1.1.1.1", "2001:db8::1"}, + serviceTypesFilter: []string{}, + annotations: map[string]string{hostnameAnnotationKey: "foobar.example.org"}, + expected: []*endpoint.Endpoint{ + {DNSName: "foobar.example.org", Targets: endpoint.Targets{"1.1.1.1", "2001:db8::1"}}, + }, + }, + { + title: "IPv6-only load-balancer service gets IPv6 endpoint", + svcNamespace: "testing", + svcName: "foobar-v6", + svcType: v1.ServiceTypeLoadBalancer, + labels: map[string]string{}, + ipFamilies: []v1.IPFamily{"IPv6"}, + clusterIP: "2001:db8::1", + externalIPs: []string{}, + lbs: []string{"2001:db8::2"}, + serviceTypesFilter: []string{}, + annotations: map[string]string{hostnameAnnotationKey: "foobar-v6.example.org"}, + expected: []*endpoint.Endpoint{ + {DNSName: "foobar-v6.example.org", Targets: endpoint.Targets{"2001:db8::2"}}, + }, + }, } { tc := tc t.Run(tc.title, func(t *testing.T) { diff --git a/source/source.go b/source/source.go index da9909923..5aceea763 100644 --- a/source/source.go +++ b/source/source.go @@ -239,8 +239,10 @@ func getTargetsFromTargetAnnotation(annotations map[string]string) endpoint.Targ // suitableType returns the DNS resource record type suitable for the target. // In this case type A for IPs and type CNAME for everything else. func suitableType(target string) string { - if net.ParseIP(target) != nil { + if net.ParseIP(target) != nil && net.ParseIP(target).To4() != nil { return endpoint.RecordTypeA + } else if net.ParseIP(target) != nil && net.ParseIP(target).To16() != nil { + return endpoint.RecordTypeAAAA } return endpoint.RecordTypeCNAME } @@ -250,12 +252,23 @@ func endpointsForHostname(hostname string, targets endpoint.Targets, ttl endpoin var endpoints []*endpoint.Endpoint var aTargets endpoint.Targets + var aaaaTargets endpoint.Targets var cnameTargets endpoint.Targets for _, t := range targets { switch suitableType(t) { case endpoint.RecordTypeA: - aTargets = append(aTargets, t) + if !isIPv6String(t) { + aTargets = append(aTargets, t) + } else { + continue + } + case endpoint.RecordTypeAAAA: + if isIPv6String(t) { + aaaaTargets = append(aaaaTargets, t) + } else { + continue + } default: cnameTargets = append(cnameTargets, t) } @@ -274,6 +287,19 @@ func endpointsForHostname(hostname string, targets endpoint.Targets, ttl endpoin endpoints = append(endpoints, epA) } + if len(aaaaTargets) > 0 { + epAAAA := &endpoint.Endpoint{ + DNSName: strings.TrimSuffix(hostname, "."), + Targets: aaaaTargets, + RecordTTL: ttl, + RecordType: endpoint.RecordTypeAAAA, + Labels: endpoint.NewLabels(), + ProviderSpecific: providerSpecific, + SetIdentifier: setIdentifier, + } + endpoints = append(endpoints, epAAAA) + } + if len(cnameTargets) > 0 { epCNAME := &endpoint.Endpoint{ DNSName: strings.TrimSuffix(hostname, "."), @@ -286,7 +312,6 @@ func endpointsForHostname(hostname string, targets endpoint.Targets, ttl endpoin } endpoints = append(endpoints, epCNAME) } - return endpoints } @@ -348,3 +373,9 @@ func waitForDynamicCacheSync(ctx context.Context, factory dynamicInformerFactory } return nil } + +// isIPv6String returns if ip is IPv6. +func isIPv6String(ip string) bool { + netIP := net.ParseIP(ip) + return netIP != nil && netIP.To4() == nil +} diff --git a/source/source_test.go b/source/source_test.go index 0c31b93ba..d6befe804 100644 --- a/source/source_test.go +++ b/source/source_test.go @@ -94,6 +94,7 @@ func TestSuitableType(t *testing.T) { target, recordType, expected string }{ {"8.8.8.8", "", "A"}, + {"2001:db8::1", "", "AAAA"}, {"foo.example.org", "", "CNAME"}, {"bar.eu-central-1.elb.amazonaws.com", "", "CNAME"}, } {