Add alias annotation for ingress

This commit is contained in:
vaegt 2018-10-16 13:30:07 +02:00
parent db13a3d5d7
commit bb80f99e17
No known key found for this signature in database
GPG Key ID: D6EB0FF4A03B395F
6 changed files with 159 additions and 18 deletions

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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: "",

View File

@ -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
@ -119,22 +133,24 @@ func endpointsForHostname(hostname string, targets endpoint.Targets, ttl endpoin
if len(aTargets) > 0 {
epA := &endpoint.Endpoint{
DNSName: strings.TrimSuffix(hostname, "."),
Targets: aTargets,
RecordTTL: ttl,
RecordType: endpoint.RecordTypeA,
Labels: endpoint.NewLabels(),
DNSName: strings.TrimSuffix(hostname, "."),
Targets: aTargets,
RecordTTL: ttl,
RecordType: endpoint.RecordTypeA,
Labels: endpoint.NewLabels(),
ProviderSpecific: providerSpecific,
}
endpoints = append(endpoints, epA)
}
if len(cnameTargets) > 0 {
epCNAME := &endpoint.Endpoint{
DNSName: strings.TrimSuffix(hostname, "."),
Targets: cnameTargets,
RecordTTL: ttl,
RecordType: endpoint.RecordTypeCNAME,
Labels: endpoint.NewLabels(),
DNSName: strings.TrimSuffix(hostname, "."),
Targets: cnameTargets,
RecordTTL: ttl,
RecordType: endpoint.RecordTypeCNAME,
Labels: endpoint.NewLabels(),
ProviderSpecific: providerSpecific,
}
endpoints = append(endpoints, epCNAME)
}