mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-06 01:26:59 +02:00
Add alias annotation for ingress
This commit is contained in:
parent
db13a3d5d7
commit
bb80f99e17
@ -364,6 +364,11 @@ func (p *AWSProvider) newChange(action string, endpoint *endpoint.Endpoint) *rou
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rec, err := p.Records()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("getting records failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
if isAWSLoadBalancer(endpoint) {
|
if isAWSLoadBalancer(endpoint) {
|
||||||
evalTargetHealth := p.evaluateTargetHealth
|
evalTargetHealth := p.evaluateTargetHealth
|
||||||
if _, ok := endpoint.ProviderSpecific[providerSpecificEvaluateTargetHealth]; ok {
|
if _, ok := endpoint.ProviderSpecific[providerSpecificEvaluateTargetHealth]; ok {
|
||||||
@ -376,6 +381,20 @@ func (p *AWSProvider) newChange(action string, endpoint *endpoint.Endpoint) *rou
|
|||||||
HostedZoneId: aws.String(canonicalHostedZone(endpoint.Targets[0])),
|
HostedZoneId: aws.String(canonicalHostedZone(endpoint.Targets[0])),
|
||||||
EvaluateTargetHealth: aws.Bool(evalTargetHealth),
|
EvaluateTargetHealth: aws.Bool(evalTargetHealth),
|
||||||
}
|
}
|
||||||
|
} else if hostedZone := isAWSAlias(endpoint, rec); hostedZone != "" {
|
||||||
|
//FIXME should break if err != nil
|
||||||
|
zones, err := p.Zones()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("getting zones failed: %v", err)
|
||||||
|
}
|
||||||
|
for _, zone := range zones {
|
||||||
|
change.ResourceRecordSet.Type = aws.String(route53.RRTypeA)
|
||||||
|
change.ResourceRecordSet.AliasTarget = &route53.AliasTarget{
|
||||||
|
DNSName: aws.String(endpoint.Targets[0]),
|
||||||
|
HostedZoneId: aws.String(cleanZoneID(*zone.Id)),
|
||||||
|
EvaluateTargetHealth: aws.Bool(p.evaluateTargetHealth),
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
change.ResourceRecordSet.Type = aws.String(endpoint.RecordType)
|
change.ResourceRecordSet.Type = aws.String(endpoint.RecordType)
|
||||||
if !endpoint.RecordTTL.IsConfigured() {
|
if !endpoint.RecordTTL.IsConfigured() {
|
||||||
@ -529,6 +548,21 @@ func isAWSLoadBalancer(ep *endpoint.Endpoint) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isAWSAlias determines if a given hostname belongs to an AWS Alias record by doing an reverse lookup.
|
||||||
|
func isAWSAlias(ep *endpoint.Endpoint, addrs []*endpoint.Endpoint) string {
|
||||||
|
if val, exists := ep.ProviderSpecific["alias"]; ep.RecordType == endpoint.RecordTypeCNAME && exists && val == "true" {
|
||||||
|
for _, addr := range addrs {
|
||||||
|
if addr.DNSName == ep.Targets[0] {
|
||||||
|
if hostedZone := canonicalHostedZone(addr.Targets[0]); hostedZone != "" {
|
||||||
|
return hostedZone
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
// canonicalHostedZone returns the matching canonical zone for a given hostname.
|
// canonicalHostedZone returns the matching canonical zone for a given hostname.
|
||||||
func canonicalHostedZone(hostname string) string {
|
func canonicalHostedZone(hostname string) string {
|
||||||
for suffix, zone := range canonicalHostedZones {
|
for suffix, zone := range canonicalHostedZones {
|
||||||
@ -539,3 +573,11 @@ func canonicalHostedZone(hostname string) string {
|
|||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// cleanZoneID removes the "/hostedzone/" prefix
|
||||||
|
func cleanZoneID(ID string) string {
|
||||||
|
if strings.HasPrefix(ID, "/hostedzone/") {
|
||||||
|
ID = strings.TrimPrefix(ID, "/hostedzone/")
|
||||||
|
}
|
||||||
|
return ID
|
||||||
|
}
|
||||||
|
@ -819,6 +819,35 @@ func TestAWSisLoadBalancer(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAWSisAWSAlias(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
target string
|
||||||
|
recordType string
|
||||||
|
alias string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{"bar.example.org", endpoint.RecordTypeCNAME, "true", "Z215JYRZR1TBD5"},
|
||||||
|
{"foo.example.org", endpoint.RecordTypeCNAME, "true", ""},
|
||||||
|
} {
|
||||||
|
ep := &endpoint.Endpoint{
|
||||||
|
Targets: endpoint.Targets{tc.target},
|
||||||
|
RecordType: tc.recordType,
|
||||||
|
ProviderSpecific: map[string]string{"alias": tc.alias},
|
||||||
|
}
|
||||||
|
addrs := []*endpoint.Endpoint{
|
||||||
|
&endpoint.Endpoint{
|
||||||
|
DNSName: "foo.example.org",
|
||||||
|
Targets: endpoint.Targets{"foobar.example.org"},
|
||||||
|
},
|
||||||
|
&endpoint.Endpoint{
|
||||||
|
DNSName: "bar.example.org",
|
||||||
|
Targets: endpoint.Targets{"bar.eu-central-1.elb.amazonaws.com"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
assert.Equal(t, tc.expected, isAWSAlias(ep, addrs))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestAWSCanonicalHostedZone(t *testing.T) {
|
func TestAWSCanonicalHostedZone(t *testing.T) {
|
||||||
for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
hostname string
|
hostname string
|
||||||
|
@ -172,12 +172,14 @@ func (sc *gatewaySource) endpointsFromTemplate(config *istiomodel.Config) ([]*en
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
providerSpecific := getProviderSpecificAnnotations(config.Annotations)
|
||||||
|
|
||||||
var endpoints []*endpoint.Endpoint
|
var endpoints []*endpoint.Endpoint
|
||||||
// splits the FQDN template and removes the trailing periods
|
// splits the FQDN template and removes the trailing periods
|
||||||
hostnameList := strings.Split(strings.Replace(hostnames, " ", "", -1), ",")
|
hostnameList := strings.Split(strings.Replace(hostnames, " ", "", -1), ",")
|
||||||
for _, hostname := range hostnameList {
|
for _, hostname := range hostnameList {
|
||||||
hostname = strings.TrimSuffix(hostname, ".")
|
hostname = strings.TrimSuffix(hostname, ".")
|
||||||
endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl)...)
|
endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl, providerSpecific)...)
|
||||||
}
|
}
|
||||||
return endpoints, nil
|
return endpoints, nil
|
||||||
}
|
}
|
||||||
@ -256,18 +258,20 @@ func (sc *gatewaySource) endpointsFromGatewayConfig(config istiomodel.Config) ([
|
|||||||
|
|
||||||
gateway := config.Spec.(*istionetworking.Gateway)
|
gateway := config.Spec.(*istionetworking.Gateway)
|
||||||
|
|
||||||
|
providerSpecific := getProviderSpecificAnnotations(config.Annotations)
|
||||||
|
|
||||||
for _, server := range gateway.Servers {
|
for _, server := range gateway.Servers {
|
||||||
for _, host := range server.Hosts {
|
for _, host := range server.Hosts {
|
||||||
if host == "" {
|
if host == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
endpoints = append(endpoints, endpointsForHostname(host, targets, ttl)...)
|
endpoints = append(endpoints, endpointsForHostname(host, targets, ttl, providerSpecific)...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hostnameList := getHostnamesFromAnnotations(config.Annotations)
|
hostnameList := getHostnamesFromAnnotations(config.Annotations)
|
||||||
for _, hostname := range hostnameList {
|
for _, hostname := range hostnameList {
|
||||||
endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl)...)
|
endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl, providerSpecific)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
return endpoints, nil
|
return endpoints, nil
|
||||||
|
@ -146,12 +146,14 @@ func (sc *ingressSource) endpointsFromTemplate(ing *v1beta1.Ingress) ([]*endpoin
|
|||||||
targets = targetsFromIngressStatus(ing.Status)
|
targets = targetsFromIngressStatus(ing.Status)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
providerSpecific := getProviderSpecificAnnotations(ing.Annotations)
|
||||||
|
|
||||||
var endpoints []*endpoint.Endpoint
|
var endpoints []*endpoint.Endpoint
|
||||||
// splits the FQDN template and removes the trailing periods
|
// splits the FQDN template and removes the trailing periods
|
||||||
hostnameList := strings.Split(strings.Replace(hostnames, " ", "", -1), ",")
|
hostnameList := strings.Split(strings.Replace(hostnames, " ", "", -1), ",")
|
||||||
for _, hostname := range hostnameList {
|
for _, hostname := range hostnameList {
|
||||||
hostname = strings.TrimSuffix(hostname, ".")
|
hostname = strings.TrimSuffix(hostname, ".")
|
||||||
endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl)...)
|
endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl, providerSpecific)...)
|
||||||
}
|
}
|
||||||
return endpoints, nil
|
return endpoints, nil
|
||||||
}
|
}
|
||||||
@ -208,11 +210,13 @@ func endpointsFromIngress(ing *v1beta1.Ingress) []*endpoint.Endpoint {
|
|||||||
targets = targetsFromIngressStatus(ing.Status)
|
targets = targetsFromIngressStatus(ing.Status)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
providerSpecific := getProviderSpecificAnnotations(ing.Annotations)
|
||||||
|
|
||||||
for _, rule := range ing.Spec.Rules {
|
for _, rule := range ing.Spec.Rules {
|
||||||
if rule.Host == "" {
|
if rule.Host == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
endpoints = append(endpoints, endpointsForHostname(rule.Host, targets, ttl)...)
|
endpoints = append(endpoints, endpointsForHostname(rule.Host, targets, ttl, providerSpecific)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tls := range ing.Spec.TLS {
|
for _, tls := range ing.Spec.TLS {
|
||||||
@ -220,13 +224,13 @@ func endpointsFromIngress(ing *v1beta1.Ingress) []*endpoint.Endpoint {
|
|||||||
if host == "" {
|
if host == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
endpoints = append(endpoints, endpointsForHostname(host, targets, ttl)...)
|
endpoints = append(endpoints, endpointsForHostname(host, targets, ttl, providerSpecific)...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hostnameList := getHostnamesFromAnnotations(ing.Annotations)
|
hostnameList := getHostnamesFromAnnotations(ing.Annotations)
|
||||||
for _, hostname := range hostnameList {
|
for _, hostname := range hostnameList {
|
||||||
endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl)...)
|
endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl, providerSpecific)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
return endpoints
|
return endpoints
|
||||||
|
@ -817,6 +817,52 @@ func testIngressEndpoints(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "ingress rules with alias and target annotation",
|
||||||
|
targetNamespace: "",
|
||||||
|
ingressItems: []fakeIngress{
|
||||||
|
{
|
||||||
|
name: "fake1",
|
||||||
|
namespace: namespace,
|
||||||
|
annotations: map[string]string{
|
||||||
|
targetAnnotationKey: "ingress-target.com",
|
||||||
|
aliasAnnotationKey: "true",
|
||||||
|
},
|
||||||
|
dnsnames: []string{"example.org"},
|
||||||
|
ips: []string{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: []*endpoint.Endpoint{
|
||||||
|
{
|
||||||
|
DNSName: "example.org",
|
||||||
|
Targets: endpoint.Targets{"ingress-target.com"},
|
||||||
|
RecordType: endpoint.RecordTypeCNAME,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "ingress rules with alias set false and target annotation",
|
||||||
|
targetNamespace: "",
|
||||||
|
ingressItems: []fakeIngress{
|
||||||
|
{
|
||||||
|
name: "fake1",
|
||||||
|
namespace: namespace,
|
||||||
|
annotations: map[string]string{
|
||||||
|
targetAnnotationKey: "ingress-target.com",
|
||||||
|
aliasAnnotationKey: "false",
|
||||||
|
},
|
||||||
|
dnsnames: []string{"example.org"},
|
||||||
|
ips: []string{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: []*endpoint.Endpoint{
|
||||||
|
{
|
||||||
|
DNSName: "example.org",
|
||||||
|
Targets: endpoint.Targets{"ingress-target.com"},
|
||||||
|
RecordType: endpoint.RecordTypeCNAME,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: "template for ingress with annotation",
|
title: "template for ingress with annotation",
|
||||||
targetNamespace: "",
|
targetNamespace: "",
|
||||||
|
@ -35,6 +35,8 @@ const (
|
|||||||
targetAnnotationKey = "external-dns.alpha.kubernetes.io/target"
|
targetAnnotationKey = "external-dns.alpha.kubernetes.io/target"
|
||||||
// The annotation used for defining the desired DNS record TTL
|
// The annotation used for defining the desired DNS record TTL
|
||||||
ttlAnnotationKey = "external-dns.alpha.kubernetes.io/ttl"
|
ttlAnnotationKey = "external-dns.alpha.kubernetes.io/ttl"
|
||||||
|
//// The annotation used for switching to the alias record types e. g. AWS Alias records instead of a normal CNAME
|
||||||
|
aliasAnnotationKey = "external-dns.alpha.kubernetes.io/alias"
|
||||||
// The value of the controller annotation so that we feel responsible
|
// The value of the controller annotation so that we feel responsible
|
||||||
controllerAnnotationValue = "dns-controller"
|
controllerAnnotationValue = "dns-controller"
|
||||||
)
|
)
|
||||||
@ -74,6 +76,18 @@ func getHostnamesFromAnnotations(annotations map[string]string) []string {
|
|||||||
return strings.Split(strings.Replace(hostnameAnnotation, " ", "", -1), ",")
|
return strings.Split(strings.Replace(hostnameAnnotation, " ", "", -1), ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getAliasFromAnnotations(annotations map[string]string) bool {
|
||||||
|
aliasAnnotation, exists := annotations[aliasAnnotationKey]
|
||||||
|
return exists && aliasAnnotation == "true"
|
||||||
|
}
|
||||||
|
|
||||||
|
func getProviderSpecificAnnotations(annotations map[string]string) endpoint.ProviderSpecific {
|
||||||
|
if getAliasFromAnnotations(annotations) {
|
||||||
|
return map[string]string{"alias": "true"}
|
||||||
|
}
|
||||||
|
return map[string]string{}
|
||||||
|
}
|
||||||
|
|
||||||
// getTargetsFromTargetAnnotation gets endpoints from optional "target" annotation.
|
// getTargetsFromTargetAnnotation gets endpoints from optional "target" annotation.
|
||||||
// Returns empty endpoints array if none are found.
|
// Returns empty endpoints array if none are found.
|
||||||
func getTargetsFromTargetAnnotation(annotations map[string]string) endpoint.Targets {
|
func getTargetsFromTargetAnnotation(annotations map[string]string) endpoint.Targets {
|
||||||
@ -102,7 +116,7 @@ func suitableType(target string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// endpointsForHostname returns the endpoint objects for each host-target combination.
|
// endpointsForHostname returns the endpoint objects for each host-target combination.
|
||||||
func endpointsForHostname(hostname string, targets endpoint.Targets, ttl endpoint.TTL) []*endpoint.Endpoint {
|
func endpointsForHostname(hostname string, targets endpoint.Targets, ttl endpoint.TTL, providerSpecific endpoint.ProviderSpecific) []*endpoint.Endpoint {
|
||||||
var endpoints []*endpoint.Endpoint
|
var endpoints []*endpoint.Endpoint
|
||||||
|
|
||||||
var aTargets endpoint.Targets
|
var aTargets endpoint.Targets
|
||||||
@ -119,22 +133,24 @@ func endpointsForHostname(hostname string, targets endpoint.Targets, ttl endpoin
|
|||||||
|
|
||||||
if len(aTargets) > 0 {
|
if len(aTargets) > 0 {
|
||||||
epA := &endpoint.Endpoint{
|
epA := &endpoint.Endpoint{
|
||||||
DNSName: strings.TrimSuffix(hostname, "."),
|
DNSName: strings.TrimSuffix(hostname, "."),
|
||||||
Targets: aTargets,
|
Targets: aTargets,
|
||||||
RecordTTL: ttl,
|
RecordTTL: ttl,
|
||||||
RecordType: endpoint.RecordTypeA,
|
RecordType: endpoint.RecordTypeA,
|
||||||
Labels: endpoint.NewLabels(),
|
Labels: endpoint.NewLabels(),
|
||||||
|
ProviderSpecific: providerSpecific,
|
||||||
}
|
}
|
||||||
endpoints = append(endpoints, epA)
|
endpoints = append(endpoints, epA)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(cnameTargets) > 0 {
|
if len(cnameTargets) > 0 {
|
||||||
epCNAME := &endpoint.Endpoint{
|
epCNAME := &endpoint.Endpoint{
|
||||||
DNSName: strings.TrimSuffix(hostname, "."),
|
DNSName: strings.TrimSuffix(hostname, "."),
|
||||||
Targets: cnameTargets,
|
Targets: cnameTargets,
|
||||||
RecordTTL: ttl,
|
RecordTTL: ttl,
|
||||||
RecordType: endpoint.RecordTypeCNAME,
|
RecordType: endpoint.RecordTypeCNAME,
|
||||||
Labels: endpoint.NewLabels(),
|
Labels: endpoint.NewLabels(),
|
||||||
|
ProviderSpecific: providerSpecific,
|
||||||
}
|
}
|
||||||
endpoints = append(endpoints, epCNAME)
|
endpoints = append(endpoints, epCNAME)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user