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) {
|
||||
evalTargetHealth := p.evaluateTargetHealth
|
||||
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])),
|
||||
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 {
|
||||
change.ResourceRecordSet.Type = aws.String(endpoint.RecordType)
|
||||
if !endpoint.RecordTTL.IsConfigured() {
|
||||
@ -529,6 +548,21 @@ func isAWSLoadBalancer(ep *endpoint.Endpoint) bool {
|
||||
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.
|
||||
func canonicalHostedZone(hostname string) string {
|
||||
for suffix, zone := range canonicalHostedZones {
|
||||
@ -539,3 +573,11 @@ func canonicalHostedZone(hostname string) string {
|
||||
|
||||
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) {
|
||||
for _, tc := range []struct {
|
||||
hostname string
|
||||
|
@ -172,12 +172,14 @@ func (sc *gatewaySource) endpointsFromTemplate(config *istiomodel.Config) ([]*en
|
||||
}
|
||||
}
|
||||
|
||||
providerSpecific := getProviderSpecificAnnotations(config.Annotations)
|
||||
|
||||
var endpoints []*endpoint.Endpoint
|
||||
// splits the FQDN template and removes the trailing periods
|
||||
hostnameList := strings.Split(strings.Replace(hostnames, " ", "", -1), ",")
|
||||
for _, hostname := range hostnameList {
|
||||
hostname = strings.TrimSuffix(hostname, ".")
|
||||
endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl)...)
|
||||
endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl, providerSpecific)...)
|
||||
}
|
||||
return endpoints, nil
|
||||
}
|
||||
@ -256,18 +258,20 @@ func (sc *gatewaySource) endpointsFromGatewayConfig(config istiomodel.Config) ([
|
||||
|
||||
gateway := config.Spec.(*istionetworking.Gateway)
|
||||
|
||||
providerSpecific := getProviderSpecificAnnotations(config.Annotations)
|
||||
|
||||
for _, server := range gateway.Servers {
|
||||
for _, host := range server.Hosts {
|
||||
if host == "" {
|
||||
continue
|
||||
}
|
||||
endpoints = append(endpoints, endpointsForHostname(host, targets, ttl)...)
|
||||
endpoints = append(endpoints, endpointsForHostname(host, targets, ttl, providerSpecific)...)
|
||||
}
|
||||
}
|
||||
|
||||
hostnameList := getHostnamesFromAnnotations(config.Annotations)
|
||||
for _, hostname := range hostnameList {
|
||||
endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl)...)
|
||||
endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl, providerSpecific)...)
|
||||
}
|
||||
|
||||
return endpoints, nil
|
||||
|
@ -146,12 +146,14 @@ func (sc *ingressSource) endpointsFromTemplate(ing *v1beta1.Ingress) ([]*endpoin
|
||||
targets = targetsFromIngressStatus(ing.Status)
|
||||
}
|
||||
|
||||
providerSpecific := getProviderSpecificAnnotations(ing.Annotations)
|
||||
|
||||
var endpoints []*endpoint.Endpoint
|
||||
// splits the FQDN template and removes the trailing periods
|
||||
hostnameList := strings.Split(strings.Replace(hostnames, " ", "", -1), ",")
|
||||
for _, hostname := range hostnameList {
|
||||
hostname = strings.TrimSuffix(hostname, ".")
|
||||
endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl)...)
|
||||
endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl, providerSpecific)...)
|
||||
}
|
||||
return endpoints, nil
|
||||
}
|
||||
@ -208,11 +210,13 @@ func endpointsFromIngress(ing *v1beta1.Ingress) []*endpoint.Endpoint {
|
||||
targets = targetsFromIngressStatus(ing.Status)
|
||||
}
|
||||
|
||||
providerSpecific := getProviderSpecificAnnotations(ing.Annotations)
|
||||
|
||||
for _, rule := range ing.Spec.Rules {
|
||||
if rule.Host == "" {
|
||||
continue
|
||||
}
|
||||
endpoints = append(endpoints, endpointsForHostname(rule.Host, targets, ttl)...)
|
||||
endpoints = append(endpoints, endpointsForHostname(rule.Host, targets, ttl, providerSpecific)...)
|
||||
}
|
||||
|
||||
for _, tls := range ing.Spec.TLS {
|
||||
@ -220,13 +224,13 @@ func endpointsFromIngress(ing *v1beta1.Ingress) []*endpoint.Endpoint {
|
||||
if host == "" {
|
||||
continue
|
||||
}
|
||||
endpoints = append(endpoints, endpointsForHostname(host, targets, ttl)...)
|
||||
endpoints = append(endpoints, endpointsForHostname(host, targets, ttl, providerSpecific)...)
|
||||
}
|
||||
}
|
||||
|
||||
hostnameList := getHostnamesFromAnnotations(ing.Annotations)
|
||||
for _, hostname := range hostnameList {
|
||||
endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl)...)
|
||||
endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl, providerSpecific)...)
|
||||
}
|
||||
|
||||
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",
|
||||
targetNamespace: "",
|
||||
|
@ -35,6 +35,8 @@ const (
|
||||
targetAnnotationKey = "external-dns.alpha.kubernetes.io/target"
|
||||
// The annotation used for defining the desired DNS record 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
|
||||
controllerAnnotationValue = "dns-controller"
|
||||
)
|
||||
@ -74,6 +76,18 @@ func getHostnamesFromAnnotations(annotations map[string]string) []string {
|
||||
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.
|
||||
// Returns empty endpoints array if none are found.
|
||||
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.
|
||||
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 aTargets endpoint.Targets
|
||||
@ -124,6 +138,7 @@ func endpointsForHostname(hostname string, targets endpoint.Targets, ttl endpoin
|
||||
RecordTTL: ttl,
|
||||
RecordType: endpoint.RecordTypeA,
|
||||
Labels: endpoint.NewLabels(),
|
||||
ProviderSpecific: providerSpecific,
|
||||
}
|
||||
endpoints = append(endpoints, epA)
|
||||
}
|
||||
@ -135,6 +150,7 @@ func endpointsForHostname(hostname string, targets endpoint.Targets, ttl endpoin
|
||||
RecordTTL: ttl,
|
||||
RecordType: endpoint.RecordTypeCNAME,
|
||||
Labels: endpoint.NewLabels(),
|
||||
ProviderSpecific: providerSpecific,
|
||||
}
|
||||
endpoints = append(endpoints, epCNAME)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user