mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-11-01 11:10:59 +01:00
Merge pull request #1696 from dansimone/dansimone/support-prefer-ingress-annotations
Optional ability to use the host name defined on an ingress's annotations *instead* of its hosts stanza
This commit is contained in:
commit
2f78d09b47
@ -40,7 +40,9 @@ Services exposed via `type=LoadBalancer`, `type=ExternalName` and for the hostna
|
||||
|
||||
There are three sources of information for ExternalDNS to decide on DNS name. ExternalDNS will pick one in order as listed below:
|
||||
|
||||
1. For ingress objects ExternalDNS will create a DNS record based on the host specified for the ingress object. For services ExternalDNS will look for the annotation `external-dns.alpha.kubernetes.io/hostname` on the service and use the loadbalancer IP, it also will look for the annotation `external-dns.alpha.kubernetes.io/internal-hostname` on the service and use the service IP.
|
||||
1. For ingress objects ExternalDNS will create a DNS record based on the hosts specified for the ingress object, as well as the `external-dns.alpha.kubernetes.io/hostname` annotation. For services ExternalDNS will look for the annotation `external-dns.alpha.kubernetes.io/hostname` on the service and use the loadbalancer IP, it also will look for the annotation `external-dns.alpha.kubernetes.io/internal-hostname` on the service and use the service IP.
|
||||
- For ingresses, you can optionally force ExternalDNS to create records based on _either_ the hosts specified or the `external-dns.alpha.kubernetes.io/hostname` annotation. This behavior is controlled by
|
||||
setting the `external-dns.alpha.kubernetes.io/ingress-hostname-source` annotation on that ingress to either `defined-hosts-only` or `annotation-only`.
|
||||
|
||||
2. If compatibility mode is enabled (e.g. `--compatibility={mate,molecule}` flag), External DNS will parse annotations used by Zalando/Mate, wearemolecule/route53-kubernetes. Compatibility mode with Kops DNS Controller is planned to be added in the future.
|
||||
|
||||
|
||||
@ -42,6 +42,10 @@ const (
|
||||
ALBDualstackAnnotationKey = "alb.ingress.kubernetes.io/ip-address-type"
|
||||
// ALBDualstackAnnotationValue is the value of the ALB dualstack annotation that indicates it is dualstack
|
||||
ALBDualstackAnnotationValue = "dualstack"
|
||||
|
||||
// Possible values for the ingress-hostname-source annotation
|
||||
IngressHostnameSourceAnnotationOnlyValue = "annotation-only"
|
||||
IngressHostnameSourceDefinedHostsOnlyValue = "defined-hosts-only"
|
||||
)
|
||||
|
||||
// ingressSource is an implementation of Source for Kubernetes ingress objects.
|
||||
@ -243,8 +247,6 @@ func (sc *ingressSource) setDualstackLabel(ingress *v1beta1.Ingress, endpoints [
|
||||
|
||||
// endpointsFromIngress extracts the endpoints from ingress object
|
||||
func endpointsFromIngress(ing *v1beta1.Ingress, ignoreHostnameAnnotation bool, ignoreIngressTLSSpec bool) []*endpoint.Endpoint {
|
||||
var endpoints []*endpoint.Endpoint
|
||||
|
||||
ttl, err := getTTLFromAnnotations(ing.Annotations)
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
@ -258,11 +260,13 @@ func endpointsFromIngress(ing *v1beta1.Ingress, ignoreHostnameAnnotation bool, i
|
||||
|
||||
providerSpecific, setIdentifier := getProviderSpecificAnnotations(ing.Annotations)
|
||||
|
||||
// Gather endpoints defined on hosts sections of the ingress
|
||||
var definedHostsEndpoints []*endpoint.Endpoint
|
||||
for _, rule := range ing.Spec.Rules {
|
||||
if rule.Host == "" {
|
||||
continue
|
||||
}
|
||||
endpoints = append(endpoints, endpointsForHostname(rule.Host, targets, ttl, providerSpecific, setIdentifier)...)
|
||||
definedHostsEndpoints = append(definedHostsEndpoints, endpointsForHostname(rule.Host, targets, ttl, providerSpecific, setIdentifier)...)
|
||||
}
|
||||
|
||||
// Skip endpoints if we do not want entries from tls spec section
|
||||
@ -272,18 +276,33 @@ func endpointsFromIngress(ing *v1beta1.Ingress, ignoreHostnameAnnotation bool, i
|
||||
if host == "" {
|
||||
continue
|
||||
}
|
||||
endpoints = append(endpoints, endpointsForHostname(host, targets, ttl, providerSpecific, setIdentifier)...)
|
||||
definedHostsEndpoints = append(definedHostsEndpoints, endpointsForHostname(host, targets, ttl, providerSpecific, setIdentifier)...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Skip endpoints if we do not want entries from annotations
|
||||
// Gather endpoints defined on annotations in the ingress
|
||||
var annotationEndpoints []*endpoint.Endpoint
|
||||
if !ignoreHostnameAnnotation {
|
||||
hostnameList := getHostnamesFromAnnotations(ing.Annotations)
|
||||
for _, hostname := range hostnameList {
|
||||
endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl, providerSpecific, setIdentifier)...)
|
||||
for _, hostname := range getHostnamesFromAnnotations(ing.Annotations) {
|
||||
annotationEndpoints = append(annotationEndpoints, endpointsForHostname(hostname, targets, ttl, providerSpecific, setIdentifier)...)
|
||||
}
|
||||
}
|
||||
|
||||
// Determine which hostnames to consider in our final list
|
||||
hostnameSourceAnnotation, hostnameSourceAnnotationExists := ing.Annotations[ingressHostnameSourceKey]
|
||||
if !hostnameSourceAnnotationExists {
|
||||
return append(definedHostsEndpoints, annotationEndpoints...)
|
||||
}
|
||||
|
||||
// Include endpoints according to the hostname source annotation in our final list
|
||||
var endpoints []*endpoint.Endpoint
|
||||
if strings.ToLower(hostnameSourceAnnotation) == IngressHostnameSourceDefinedHostsOnlyValue {
|
||||
endpoints = append(endpoints, definedHostsEndpoints...)
|
||||
}
|
||||
if strings.ToLower(hostnameSourceAnnotation) == IngressHostnameSourceAnnotationOnlyValue {
|
||||
endpoints = append(endpoints, annotationEndpoints...)
|
||||
}
|
||||
return endpoints
|
||||
}
|
||||
|
||||
|
||||
@ -85,6 +85,7 @@ func (suite *IngressSuite) TestDualstackLabelIsSet() {
|
||||
func TestIngress(t *testing.T) {
|
||||
suite.Run(t, new(IngressSuite))
|
||||
t.Run("endpointsFromIngress", testEndpointsFromIngress)
|
||||
t.Run("endpointsFromIngressHostnameSourceAnnotation", testEndpointsFromIngressHostnameSourceAnnotation)
|
||||
t.Run("Endpoints", testIngressEndpoints)
|
||||
}
|
||||
|
||||
@ -228,6 +229,98 @@ func testEndpointsFromIngress(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func testEndpointsFromIngressHostnameSourceAnnotation(t *testing.T) {
|
||||
// Host names and host name annotation provided, with various values of the ingress-hostname-source annotation
|
||||
for _, ti := range []struct {
|
||||
title string
|
||||
ingress fakeIngress
|
||||
expected []*endpoint.Endpoint
|
||||
}{
|
||||
{
|
||||
title: "No ingress-hostname-source annotation, one rule.host, one annotation host",
|
||||
ingress: fakeIngress{
|
||||
dnsnames: []string{"foo.bar"},
|
||||
annotations: map[string]string{hostnameAnnotationKey: "foo.baz"},
|
||||
hostnames: []string{"lb.com"},
|
||||
},
|
||||
expected: []*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "foo.bar",
|
||||
Targets: endpoint.Targets{"lb.com"},
|
||||
},
|
||||
{
|
||||
DNSName: "foo.baz",
|
||||
Targets: endpoint.Targets{"lb.com"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "No ingress-hostname-source annotation, one rule.host",
|
||||
ingress: fakeIngress{
|
||||
dnsnames: []string{"foo.bar"},
|
||||
hostnames: []string{"lb.com"},
|
||||
},
|
||||
expected: []*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "foo.bar",
|
||||
Targets: endpoint.Targets{"lb.com"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "No ingress-hostname-source annotation, one rule.host, one annotation host",
|
||||
ingress: fakeIngress{
|
||||
dnsnames: []string{"foo.bar"},
|
||||
annotations: map[string]string{hostnameAnnotationKey: "foo.baz"},
|
||||
hostnames: []string{"lb.com"},
|
||||
},
|
||||
expected: []*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "foo.bar",
|
||||
Targets: endpoint.Targets{"lb.com"},
|
||||
},
|
||||
{
|
||||
DNSName: "foo.baz",
|
||||
Targets: endpoint.Targets{"lb.com"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Ingress-hostname-source=defined-hosts-only, one rule.host, one annotation host",
|
||||
ingress: fakeIngress{
|
||||
dnsnames: []string{"foo.bar"},
|
||||
annotations: map[string]string{hostnameAnnotationKey: "foo.baz", ingressHostnameSourceKey: "defined-hosts-only"},
|
||||
hostnames: []string{"lb.com"},
|
||||
},
|
||||
expected: []*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "foo.bar",
|
||||
Targets: endpoint.Targets{"lb.com"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Ingress-hostname-source=annotation-only, one rule.host, one annotation host",
|
||||
ingress: fakeIngress{
|
||||
dnsnames: []string{"foo.bar"},
|
||||
annotations: map[string]string{hostnameAnnotationKey: "foo.baz", ingressHostnameSourceKey: "annotation-only"},
|
||||
hostnames: []string{"lb.com"},
|
||||
},
|
||||
expected: []*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "foo.baz",
|
||||
Targets: endpoint.Targets{"lb.com"},
|
||||
},
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(ti.title, func(t *testing.T) {
|
||||
realIngress := ti.ingress.Ingress()
|
||||
validateEndpoints(t, endpointsFromIngress(realIngress, false, false), ti.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func testIngressEndpoints(t *testing.T) {
|
||||
namespace := "testing"
|
||||
for _, ti := range []struct {
|
||||
|
||||
@ -46,6 +46,9 @@ const (
|
||||
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 annotation used to determine the source of hostnames for ingresses. This is an optional field - all
|
||||
// available hostname sources are used if not specified.
|
||||
ingressHostnameSourceKey = "external-dns.alpha.kubernetes.io/ingress-hostname-source"
|
||||
// The value of the controller annotation so that we feel responsible
|
||||
controllerAnnotationValue = "dns-controller"
|
||||
// The annotation used for defining the desired hostname
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user