mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-06 01:26:59 +02:00
support hostnames as endpoint targets (CNAME support) (#122)
* feat(aws): support hostnames as endpoint targets * docs: describe how to run ExternalDNS on AWS * docs: update changelog with CNAME feature * docs: update changelog to include AWS documentation * fix(aws): test that updating records removes the old value * feat(google): add CNAME support to Google provider * fix(source): sanitize source and target hostnames * docs: update changelog to include latest changes * docs(aws): mention that ExternalDNS takes full ownership of a hosted zone * fix(aws): switch route53 tests to use endpoint pointers * docs: add TODO to remove record filtering once ownership is in place
This commit is contained in:
parent
5c794e1b39
commit
b0f437a438
@ -1,7 +1,16 @@
|
||||
Features:
|
||||
|
||||
- Support creation of CNAME records when endpoint target is a hostname.
|
||||
- Allow omitting the trailing dot in Service annotations.
|
||||
|
||||
Bug fixes:
|
||||
|
||||
- AWS Route 53: Do not submit request when there are no changes.
|
||||
|
||||
Documentation:
|
||||
|
||||
- Add documentation on how to setup ExternalDNS for Services on AWS.
|
||||
|
||||
## v0.1.0 - 2017-03-30 (KubeCon)
|
||||
|
||||
Features:
|
||||
|
@ -42,7 +42,7 @@ $ kubectl run nginx --image=nginx --replicas=1 --port=80
|
||||
$ kubectl expose deployment nginx --port=80 --target-port=80 --type=LoadBalancer
|
||||
```
|
||||
|
||||
Annotate the Service with your desired external DNS name. Make sure to change `example.org` to your domain (and that it includes the trailing dot):
|
||||
Annotate the Service with your desired external DNS name. Make sure to change `example.org` to your domain.
|
||||
|
||||
```console
|
||||
$ kubectl annotate service nginx "external-dns.alpha.kubernetes.io/hostname=nginx.example.org."
|
||||
|
152
docs/tutorials/aws.md
Normal file
152
docs/tutorials/aws.md
Normal file
@ -0,0 +1,152 @@
|
||||
# Setting up ExternalDNS for Services on AWS
|
||||
|
||||
This tutorial describes how to setup ExternalDNS for usage within a Kubernetes cluster on AWS.
|
||||
|
||||
Create a DNS zone which will contain the managed DNS records.
|
||||
|
||||
```console
|
||||
$ aws route53 create-hosted-zone --name "external-dns-test.teapot.zalan.do." --caller-reference "external-dns-test-$(date +%s)"
|
||||
```
|
||||
|
||||
Make a note of the ID of the hosted zone you just created.
|
||||
|
||||
```console
|
||||
$ aws route53 list-hosted-zones-by-name --dns-name "external-dns-test.teapot.zalan.do." | jq -r '.HostedZones[0].Id'
|
||||
/hostedzone/Z16P7IEWFWZ4RB
|
||||
```
|
||||
|
||||
Make a note of the nameservers that were assigned to your new zone.
|
||||
|
||||
```console
|
||||
$ aws route53 list-resource-record-sets --hosted-zone-id "/hostedzone/Z16P7IEWFWZ4RB" \
|
||||
--query "ResourceRecordSets[?Type == 'NS']" | jq -r '.[0].ResourceRecords[].Value'
|
||||
ns-1455.awsdns-53.org.
|
||||
ns-1694.awsdns-19.co.uk.
|
||||
ns-764.awsdns-31.net.
|
||||
ns-62.awsdns-07.com.
|
||||
```
|
||||
|
||||
In this case it's the ones shown above but your's will differ.
|
||||
|
||||
If you decide not to create a new zone but reuse an existing one, make sure it's currently **unused** and **empty**. This version of ExternalDNS will remove all records it doesn't recognize from the zone.
|
||||
|
||||
Connect your `kubectl` client to the cluster you want to test ExternalDNS with.
|
||||
Then apply the following manifest file to deploy ExternalDNS.
|
||||
|
||||
```yaml
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: external-dns
|
||||
spec:
|
||||
strategy:
|
||||
type: Recreate
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: external-dns
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:v0.2.0-beta.0
|
||||
args:
|
||||
- --in-cluster
|
||||
- --zone=external-dns-test.teapot.zalan.do.
|
||||
- --source=service
|
||||
- --provider=aws
|
||||
- --dry-run=false
|
||||
```
|
||||
|
||||
Create the following sample application to test that ExternalDNS works.
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: nginx
|
||||
annotations:
|
||||
external-dns.alpha.kubernetes.io/hostname: nginx.external-dns-test.teapot.zalan.do.
|
||||
spec:
|
||||
type: LoadBalancer
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
selector:
|
||||
app: nginx
|
||||
|
||||
---
|
||||
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx
|
||||
name: nginx
|
||||
ports:
|
||||
- containerPort: 80
|
||||
```
|
||||
|
||||
After roughly two minutes check that a corresponding DNS record for your service was created.
|
||||
|
||||
```console
|
||||
$ aws route53 list-resource-record-sets --hosted-zone-id "/hostedzone/Z16P7IEWFWZ4RB" \
|
||||
--query "ResourceRecordSets[?Name == 'nginx.external-dns-test.teapot.zalan.do.']|[?Type == 'CNAME']"
|
||||
[
|
||||
{
|
||||
"ResourceRecords": [
|
||||
{
|
||||
"Value": "ae11c2360188411e7951602725593fd1-1224345803.eu-central-1.elb.amazonaws.com"
|
||||
}
|
||||
],
|
||||
"Type": "CNAME",
|
||||
"Name": "nginx.external-dns-test.teapot.zalan.do.",
|
||||
"TTL": 300
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Let's check that we can resolve this DNS name. We'll ask the nameservers assigned to your zone first.
|
||||
|
||||
```console
|
||||
$ dig +short @ns-1455.awsdns-53.org. nginx.external-dns-test.teapot.zalan.do.
|
||||
ae11c2360188411e7951602725593fd1-1224345803.eu-central-1.elb.amazonaws.com.
|
||||
```
|
||||
|
||||
If you hooked up your DNS zone with its parent zone correctly you can use `curl` to access your site.
|
||||
|
||||
```console
|
||||
$ curl nginx.external-dns-test.teapot.zalan.do.
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Welcome to nginx!</title>
|
||||
...
|
||||
</head>
|
||||
<body>
|
||||
...
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
Ingress objects on AWS require a separately deployed Ingress controller which we'll describe in another tutorial.
|
||||
|
||||
## Clean up
|
||||
|
||||
Make sure to delete all Service objects before terminating the cluster so all load balancers get cleaned up correctly.
|
||||
|
||||
```console
|
||||
$ kubectl delete service nginx
|
||||
```
|
||||
|
||||
Give ExternalDNS some time to clean up the DNS records for you. Then delete the hosted zone.
|
||||
|
||||
```console
|
||||
$ aws route53 delete-hosted-zone --id /hostedzone/Z16P7IEWFWZ4RB
|
||||
```
|
@ -1,6 +1,6 @@
|
||||
# Setting up ExternalDNS on Google Container Engine
|
||||
|
||||
This tutorial describes how to setup `external-dns` for usage within a GKE cluster.
|
||||
This tutorial describes how to setup ExternalDNS for usage within a GKE cluster.
|
||||
|
||||
Setup your environment to work with Google Cloud Platform. Fill in your values as needed, e.g. target project.
|
||||
|
||||
@ -48,7 +48,7 @@ $ gcloud dns record-sets transaction add ns-cloud-e{1..4}.googledomains.com. \
|
||||
$ gcloud dns record-sets transaction execute --zone "gcp-zalan-do"
|
||||
```
|
||||
|
||||
If you decide not to create a new zone but reuse an existing one, make sure it's currently **unused** and **empty**. This version of `external-dns` will remove all records it doesn't recognize from the zone.
|
||||
If you decide not to create a new zone but reuse an existing one, make sure it's currently **unused** and **empty**. This version of ExternalDNS will remove all records it doesn't recognize from the zone.
|
||||
|
||||
Connect your `kubectl` client to the cluster you just created.
|
||||
|
||||
@ -56,7 +56,7 @@ Connect your `kubectl` client to the cluster you just created.
|
||||
gcloud container clusters get-credentials "external-dns"
|
||||
```
|
||||
|
||||
Apply the following manifest file to deploy `external-dns`.
|
||||
Apply the following manifest file to deploy ExternalDNS.
|
||||
|
||||
```yaml
|
||||
apiVersion: extensions/v1beta1
|
||||
@ -86,7 +86,7 @@ spec:
|
||||
|
||||
Use `dry-run=true` if you want to be extra careful on the first run. Note, that you will not see any records created when you are running in dry-run mode. You can, however, inspect the logs and watch what would have been done.
|
||||
|
||||
Create the following sample application to test that `external-dns` works.
|
||||
Create the following sample application to test that ExternalDNS works.
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
@ -216,7 +216,7 @@ $ kubectl delete service nginx
|
||||
$ kubectl delete ingress nginx
|
||||
```
|
||||
|
||||
Give `external-dns` some time to clean up the DNS records for you. Then delete the managed zone and cluster.
|
||||
Give ExternalDNS some time to clean up the DNS records for you. Then delete the managed zone and cluster.
|
||||
|
||||
```console
|
||||
$ gcloud dns managed-zones delete "external-dns-test-gcp-zalan-do"
|
||||
|
@ -149,7 +149,12 @@ func (p *AWSProvider) Records(zone string) ([]*endpoint.Endpoint, error) {
|
||||
|
||||
f := func(resp *route53.ListResourceRecordSetsOutput, lastPage bool) (shouldContinue bool) {
|
||||
for _, r := range resp.ResourceRecordSets {
|
||||
if aws.StringValue(r.Type) != route53.RRTypeA {
|
||||
// TODO(linki, ownership): Remove once ownership system is in place.
|
||||
// See: https://github.com/kubernetes-incubator/external-dns/pull/122/files/74e2c3d3e237411e619aefc5aab694742001cdec#r109863370
|
||||
switch aws.StringValue(r.Type) {
|
||||
case route53.RRTypeA, route53.RRTypeCname:
|
||||
break
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
@ -255,7 +260,7 @@ func newChange(action string, endpoint *endpoint.Endpoint) *route53.Change {
|
||||
},
|
||||
},
|
||||
TTL: aws.Int64(300),
|
||||
Type: aws.String(route53.RRTypeA),
|
||||
Type: aws.String(suitableType(endpoint.Target)),
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@ package provider
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@ -83,6 +84,14 @@ func (r *Route53APIStub) ChangeResourceRecordSets(input *route53.ChangeResourceR
|
||||
}
|
||||
|
||||
for _, change := range input.ChangeBatch.Changes {
|
||||
if aws.StringValue(change.ResourceRecordSet.Type) == route53.RRTypeA {
|
||||
for _, rrs := range change.ResourceRecordSet.ResourceRecords {
|
||||
if net.ParseIP(aws.StringValue(rrs.Value)) == nil {
|
||||
return nil, fmt.Errorf("A records must point to IPs")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
key := aws.StringValue(change.ResourceRecordSet.Name) + "::" + aws.StringValue(change.ResourceRecordSet.Type)
|
||||
switch aws.StringValue(change.Action) {
|
||||
case route53.ChangeActionCreate:
|
||||
@ -358,6 +367,20 @@ func TestAWSUpdateRecords(t *testing.T) {
|
||||
if !found {
|
||||
t.Fatal("update-test.ext-dns-test.teapot.zalan.do. should point to 1.2.3.4")
|
||||
}
|
||||
|
||||
found = false
|
||||
|
||||
for _, r := range records {
|
||||
if r.DNSName == "update-test.ext-dns-test.teapot.zalan.do." {
|
||||
if r.Target == "8.8.8.8" {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if found {
|
||||
t.Fatal("update-test.ext-dns-test.teapot.zalan.do. should point to 1.2.3.4")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAWSDeleteRecords(t *testing.T) {
|
||||
@ -472,6 +495,20 @@ func TestAWSApply(t *testing.T) {
|
||||
t.Fatal("update-test.ext-dns-test.teapot.zalan.do. should point to 1.2.3.4")
|
||||
}
|
||||
|
||||
found = false
|
||||
|
||||
for _, r := range records {
|
||||
if r.DNSName == "update-test.ext-dns-test.teapot.zalan.do." {
|
||||
if r.Target == "8.8.8.8" {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if found {
|
||||
t.Fatal("update-test.ext-dns-test.teapot.zalan.do. should point to 1.2.3.4")
|
||||
}
|
||||
|
||||
// delete validation
|
||||
|
||||
found = false
|
||||
@ -580,6 +617,20 @@ func TestAWSUpdateRecordDryRun(t *testing.T) {
|
||||
if found {
|
||||
t.Fatal("update-test.ext-dns-test.teapot.zalan.do. should not point to 1.2.3.4")
|
||||
}
|
||||
|
||||
found = false
|
||||
|
||||
for _, r := range records {
|
||||
if r.DNSName == "update-test.ext-dns-test.teapot.zalan.do." {
|
||||
if r.Target == "8.8.8.8" {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Fatal("update-test.ext-dns-test.teapot.zalan.do. should point to 8.8.8.8")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAWSDeleteRecordDryRun(t *testing.T) {
|
||||
@ -698,6 +749,20 @@ func TestAWSApplyDryRun(t *testing.T) {
|
||||
t.Fatal("update-test.ext-dns-test.teapot.zalan.do. should not point to 1.2.3.4")
|
||||
}
|
||||
|
||||
found = false
|
||||
|
||||
for _, r := range records {
|
||||
if r.DNSName == "update-test.ext-dns-test.teapot.zalan.do." {
|
||||
if r.Target == "8.8.8.8" {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Fatal("update-test.ext-dns-test.teapot.zalan.do. should point to 8.8.8.8")
|
||||
}
|
||||
|
||||
// delete validation
|
||||
|
||||
found = false
|
||||
@ -713,6 +778,238 @@ func TestAWSApplyDryRun(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAWSCreateRecordsCNAME(t *testing.T) {
|
||||
provider := newAWSProvider(t, false)
|
||||
|
||||
_, err := provider.CreateZone("ext-dns-test.teapot.zalan.do.")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
records := []*endpoint.Endpoint{{DNSName: "create-test.ext-dns-test.teapot.zalan.do.", Target: "foo.elb.amazonaws.com"}}
|
||||
|
||||
err = provider.CreateRecords("ext-dns-test.teapot.zalan.do.", records)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
records, err = provider.Records("ext-dns-test.teapot.zalan.do.")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
found := false
|
||||
|
||||
for _, r := range records {
|
||||
if r.DNSName == "create-test.ext-dns-test.teapot.zalan.do." {
|
||||
if r.Target == "foo.elb.amazonaws.com" {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Fatal("create-test.ext-dns-test.teapot.zalan.do. should be there")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAWSUpdateRecordsCNAME(t *testing.T) {
|
||||
provider := newAWSProvider(t, false)
|
||||
|
||||
_, err := provider.CreateZone("ext-dns-test.teapot.zalan.do.")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
oldRecords := []*endpoint.Endpoint{{DNSName: "update-test.ext-dns-test.teapot.zalan.do.", Target: "foo.elb.amazonaws.com"}}
|
||||
|
||||
err = provider.CreateRecords("ext-dns-test.teapot.zalan.do.", oldRecords)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
newRecords := []*endpoint.Endpoint{{DNSName: "update-test.ext-dns-test.teapot.zalan.do.", Target: "bar.elb.amazonaws.com"}}
|
||||
|
||||
err = provider.UpdateRecords("ext-dns-test.teapot.zalan.do.", newRecords, oldRecords)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
records, err := provider.Records("ext-dns-test.teapot.zalan.do.")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
found := false
|
||||
|
||||
for _, r := range records {
|
||||
if r.DNSName == "update-test.ext-dns-test.teapot.zalan.do." {
|
||||
if r.Target == "bar.elb.amazonaws.com" {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Fatal("update-test.ext-dns-test.teapot.zalan.do. should point to bar.elb.amazonaws.com")
|
||||
}
|
||||
|
||||
found = false
|
||||
|
||||
for _, r := range records {
|
||||
if r.DNSName == "update-test.ext-dns-test.teapot.zalan.do." {
|
||||
if r.Target == "foo.elb.amazonaws.com" {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if found {
|
||||
t.Fatal("update-test.ext-dns-test.teapot.zalan.do. should point to bar.elb.amazonaws.com")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAWSDeleteRecordsCNAME(t *testing.T) {
|
||||
provider := newAWSProvider(t, false)
|
||||
|
||||
_, err := provider.CreateZone("ext-dns-test.teapot.zalan.do.")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
records := []*endpoint.Endpoint{{DNSName: "delete-test.ext-dns-test.teapot.zalan.do.", Target: "baz.elb.amazonaws.com"}}
|
||||
|
||||
err = provider.CreateRecords("ext-dns-test.teapot.zalan.do.", records)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = provider.DeleteRecords("ext-dns-test.teapot.zalan.do.", records)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
records, err = provider.Records("ext-dns-test.teapot.zalan.do.")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
found := false
|
||||
|
||||
for _, r := range records {
|
||||
if r.DNSName == "delete-test.ext-dns-test.teapot.zalan.do." {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
||||
if found {
|
||||
t.Fatal("delete-test.ext-dns-test.teapot.zalan.do. should be gone")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAWSApplyCNAME(t *testing.T) {
|
||||
provider := newAWSProvider(t, false)
|
||||
|
||||
_, err := provider.CreateZone("ext-dns-test.teapot.zalan.do.")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
updateRecords := []*endpoint.Endpoint{{DNSName: "update-test.ext-dns-test.teapot.zalan.do.", Target: "foo.elb.amazonaws.com"}}
|
||||
|
||||
err = provider.CreateRecords("ext-dns-test.teapot.zalan.do.", updateRecords)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
deleteRecords := []*endpoint.Endpoint{{DNSName: "delete-test.ext-dns-test.teapot.zalan.do.", Target: "baz.elb.amazonaws.com"}}
|
||||
|
||||
err = provider.CreateRecords("ext-dns-test.teapot.zalan.do.", deleteRecords)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
createRecords := []*endpoint.Endpoint{{DNSName: "create-test.ext-dns-test.teapot.zalan.do.", Target: "foo.elb.amazonaws.com"}}
|
||||
updateNewRecords := []*endpoint.Endpoint{{DNSName: "update-test.ext-dns-test.teapot.zalan.do.", Target: "bar.elb.amazonaws.com"}}
|
||||
|
||||
changes := &plan.Changes{
|
||||
Create: createRecords,
|
||||
UpdateNew: updateNewRecords,
|
||||
UpdateOld: updateRecords,
|
||||
Delete: deleteRecords,
|
||||
}
|
||||
|
||||
err = provider.ApplyChanges("ext-dns-test.teapot.zalan.do.", changes)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// create validation
|
||||
|
||||
records, err := provider.Records("ext-dns-test.teapot.zalan.do.")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
found := false
|
||||
|
||||
for _, r := range records {
|
||||
if r.DNSName == "create-test.ext-dns-test.teapot.zalan.do." {
|
||||
if r.Target == "foo.elb.amazonaws.com" {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Fatal("create-test.ext-dns-test.teapot.zalan.do. should be there")
|
||||
}
|
||||
|
||||
// update validation
|
||||
|
||||
found = false
|
||||
|
||||
for _, r := range records {
|
||||
if r.DNSName == "update-test.ext-dns-test.teapot.zalan.do." {
|
||||
if r.Target == "bar.elb.amazonaws.com" {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Fatal("update-test.ext-dns-test.teapot.zalan.do. should point to bar.elb.amazonaws.com")
|
||||
}
|
||||
|
||||
found = false
|
||||
|
||||
for _, r := range records {
|
||||
if r.DNSName == "update-test.ext-dns-test.teapot.zalan.do." {
|
||||
if r.Target == "foo.elb.amazonaws.com" {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if found {
|
||||
t.Fatal("update-test.ext-dns-test.teapot.zalan.do. should point to bar.elb.amazonaws.com")
|
||||
}
|
||||
|
||||
// delete validation
|
||||
|
||||
found = false
|
||||
|
||||
for _, r := range records {
|
||||
if r.DNSName == "delete-test.ext-dns-test.teapot.zalan.do." {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
||||
if found {
|
||||
t.Fatal("delete-test.ext-dns-test.teapot.zalan.do. should be gone")
|
||||
}
|
||||
}
|
||||
|
||||
func newAWSProvider(t *testing.T, dryRun bool) *AWSProvider {
|
||||
client := NewRoute53APIStub()
|
||||
|
||||
|
@ -182,7 +182,12 @@ func (p *googleProvider) DeleteZone(name string) error {
|
||||
func (p *googleProvider) Records(zone string) (endpoints []*endpoint.Endpoint, _ error) {
|
||||
f := func(resp *dns.ResourceRecordSetsListResponse) error {
|
||||
for _, r := range resp.Rrsets {
|
||||
if r.Type != "A" {
|
||||
// TODO(linki, ownership): Remove once ownership system is in place.
|
||||
// See: https://github.com/kubernetes-incubator/external-dns/pull/122/files/74e2c3d3e237411e619aefc5aab694742001cdec#r109863370
|
||||
switch r.Type {
|
||||
case "A", "CNAME":
|
||||
break
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
@ -289,7 +294,7 @@ func newRecord(endpoint *endpoint.Endpoint) *dns.ResourceRecordSet {
|
||||
Name: endpoint.DNSName,
|
||||
Rrdatas: []string{endpoint.Target},
|
||||
Ttl: 300,
|
||||
Type: "A",
|
||||
Type: suitableType(endpoint.Target),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,11 +34,16 @@ var (
|
||||
expectedRecordSets = []*dns.ResourceRecordSet{
|
||||
{
|
||||
Type: "A",
|
||||
Name: "expected",
|
||||
Rrdatas: []string{"target"},
|
||||
Name: "expected-1",
|
||||
Rrdatas: []string{"8.8.8.8"},
|
||||
},
|
||||
{
|
||||
Type: "CNAME",
|
||||
Name: "expected-2",
|
||||
Rrdatas: []string{"target.com"},
|
||||
},
|
||||
{
|
||||
Type: "NS",
|
||||
Name: "unexpected",
|
||||
Rrdatas: []string{"target"},
|
||||
},
|
||||
|
@ -17,6 +17,8 @@ limitations under the License.
|
||||
package provider
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
)
|
||||
@ -26,3 +28,13 @@ type Provider interface {
|
||||
Records(zone string) ([]*endpoint.Endpoint, error)
|
||||
ApplyChanges(zone string, changes *plan.Changes) error
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return "CNAME"
|
||||
}
|
||||
|
||||
return "A"
|
||||
}
|
||||
|
@ -17,8 +17,6 @@ limitations under the License.
|
||||
package source
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/pkg/api/v1"
|
||||
"k8s.io/client-go/pkg/apis/extensions/v1beta1"
|
||||
@ -76,15 +74,10 @@ func endpointsFromIngress(ing *v1beta1.Ingress) []*endpoint.Endpoint {
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint(sanitizeHostname(rule.Host), lb.IP))
|
||||
}
|
||||
if lb.Hostname != "" {
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint(sanitizeHostname(rule.Host), lb.Hostname))
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint(sanitizeHostname(rule.Host), sanitizeHostname(lb.Hostname)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return endpoints
|
||||
}
|
||||
|
||||
// sanitizeHostname appends a trailing dot to a hostname if it's missing.
|
||||
func sanitizeHostname(hostname string) string {
|
||||
return strings.Trim(hostname, ".") + "."
|
||||
}
|
||||
|
@ -42,13 +42,13 @@ func testEndpointsFromIngress(t *testing.T) {
|
||||
{
|
||||
title: "one rule.host one lb.hostname",
|
||||
ingress: fakeIngress{
|
||||
dnsnames: []string{"foo.bar"},
|
||||
hostnames: []string{"lb.com"},
|
||||
dnsnames: []string{"foo.bar"}, // Kubernetes requires removal of trailing dot
|
||||
hostnames: []string{"lb.com"}, // Kubernetes omits the trailing dot
|
||||
},
|
||||
expected: []*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "foo.bar.",
|
||||
Target: "lb.com",
|
||||
Target: "lb.com.",
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -83,11 +83,11 @@ func testEndpointsFromIngress(t *testing.T) {
|
||||
},
|
||||
{
|
||||
DNSName: "foo.bar.",
|
||||
Target: "elb.com",
|
||||
Target: "elb.com.",
|
||||
},
|
||||
{
|
||||
DNSName: "foo.bar.",
|
||||
Target: "alb.com",
|
||||
Target: "alb.com.",
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -159,7 +159,7 @@ func testIngressEndpoints(t *testing.T) {
|
||||
},
|
||||
{
|
||||
DNSName: "new.org.",
|
||||
Target: "lb.com",
|
||||
Target: "lb.com.",
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -187,7 +187,7 @@ func testIngressEndpoints(t *testing.T) {
|
||||
},
|
||||
{
|
||||
DNSName: "new.org.",
|
||||
Target: "lb.com",
|
||||
Target: "lb.com.",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -76,10 +76,10 @@ func endpointsFromService(svc *v1.Service) []*endpoint.Endpoint {
|
||||
// Create a corresponding endpoint for each configured external entrypoint.
|
||||
for _, lb := range svc.Status.LoadBalancer.Ingress {
|
||||
if lb.IP != "" {
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint(hostname, lb.IP))
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint(sanitizeHostname(hostname), lb.IP))
|
||||
}
|
||||
if lb.Hostname != "" {
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint(hostname, lb.Hostname))
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint(sanitizeHostname(hostname), sanitizeHostname(lb.Hostname)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,11 +59,11 @@ func testServiceEndpoints(t *testing.T) {
|
||||
"testing",
|
||||
"foo",
|
||||
map[string]string{
|
||||
hostnameAnnotationKey: "foo.example.org",
|
||||
hostnameAnnotationKey: "foo.example.org.",
|
||||
},
|
||||
[]string{"1.2.3.4"},
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "foo.example.org", Target: "1.2.3.4"},
|
||||
{DNSName: "foo.example.org.", Target: "1.2.3.4"},
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -72,11 +72,25 @@ func testServiceEndpoints(t *testing.T) {
|
||||
"testing",
|
||||
"foo",
|
||||
map[string]string{
|
||||
hostnameAnnotationKey: "foo.example.org",
|
||||
hostnameAnnotationKey: "foo.example.org.",
|
||||
},
|
||||
[]string{"lb.example.com"},
|
||||
[]string{"lb.example.com"}, // Kubernetes omits the trailing dot
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "foo.example.org", Target: "lb.example.com"},
|
||||
{DNSName: "foo.example.org.", Target: "lb.example.com."},
|
||||
},
|
||||
},
|
||||
{
|
||||
"annotated services can omit trailing dot",
|
||||
"",
|
||||
"testing",
|
||||
"foo",
|
||||
map[string]string{
|
||||
hostnameAnnotationKey: "foo.example.org", // Trailing dot is omitted
|
||||
},
|
||||
[]string{"1.2.3.4", "lb.example.com"}, // Kubernetes omits the trailing dot
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "foo.example.org.", Target: "1.2.3.4"},
|
||||
{DNSName: "foo.example.org.", Target: "lb.example.com."},
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -86,11 +100,11 @@ func testServiceEndpoints(t *testing.T) {
|
||||
"foo",
|
||||
map[string]string{
|
||||
controllerAnnotationKey: controllerAnnotationValue,
|
||||
hostnameAnnotationKey: "foo.example.org",
|
||||
hostnameAnnotationKey: "foo.example.org.",
|
||||
},
|
||||
[]string{"1.2.3.4"},
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "foo.example.org", Target: "1.2.3.4"},
|
||||
{DNSName: "foo.example.org.", Target: "1.2.3.4"},
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -100,7 +114,7 @@ func testServiceEndpoints(t *testing.T) {
|
||||
"foo",
|
||||
map[string]string{
|
||||
controllerAnnotationKey: "some-other-tool",
|
||||
hostnameAnnotationKey: "foo.example.org",
|
||||
hostnameAnnotationKey: "foo.example.org.",
|
||||
},
|
||||
[]string{"1.2.3.4"},
|
||||
[]*endpoint.Endpoint{},
|
||||
@ -111,11 +125,11 @@ func testServiceEndpoints(t *testing.T) {
|
||||
"testing",
|
||||
"foo",
|
||||
map[string]string{
|
||||
hostnameAnnotationKey: "foo.example.org",
|
||||
hostnameAnnotationKey: "foo.example.org.",
|
||||
},
|
||||
[]string{"1.2.3.4"},
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "foo.example.org", Target: "1.2.3.4"},
|
||||
{DNSName: "foo.example.org.", Target: "1.2.3.4"},
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -124,7 +138,7 @@ func testServiceEndpoints(t *testing.T) {
|
||||
"other-testing",
|
||||
"foo",
|
||||
map[string]string{
|
||||
hostnameAnnotationKey: "foo.example.org",
|
||||
hostnameAnnotationKey: "foo.example.org.",
|
||||
},
|
||||
[]string{"1.2.3.4"},
|
||||
[]*endpoint.Endpoint{},
|
||||
@ -135,11 +149,11 @@ func testServiceEndpoints(t *testing.T) {
|
||||
"other-testing",
|
||||
"foo",
|
||||
map[string]string{
|
||||
hostnameAnnotationKey: "foo.example.org",
|
||||
hostnameAnnotationKey: "foo.example.org.",
|
||||
},
|
||||
[]string{"1.2.3.4"},
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "foo.example.org", Target: "1.2.3.4"},
|
||||
{DNSName: "foo.example.org.", Target: "1.2.3.4"},
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -148,7 +162,7 @@ func testServiceEndpoints(t *testing.T) {
|
||||
"testing",
|
||||
"foo",
|
||||
map[string]string{
|
||||
hostnameAnnotationKey: "foo.example.org",
|
||||
hostnameAnnotationKey: "foo.example.org.",
|
||||
},
|
||||
[]string{},
|
||||
[]*endpoint.Endpoint{},
|
||||
@ -159,12 +173,12 @@ func testServiceEndpoints(t *testing.T) {
|
||||
"testing",
|
||||
"foo",
|
||||
map[string]string{
|
||||
hostnameAnnotationKey: "foo.example.org",
|
||||
hostnameAnnotationKey: "foo.example.org.",
|
||||
},
|
||||
[]string{"1.2.3.4", "8.8.8.8"},
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "foo.example.org", Target: "1.2.3.4"},
|
||||
{DNSName: "foo.example.org", Target: "8.8.8.8"},
|
||||
{DNSName: "foo.example.org.", Target: "1.2.3.4"},
|
||||
{DNSName: "foo.example.org.", Target: "8.8.8.8"},
|
||||
},
|
||||
},
|
||||
} {
|
||||
@ -222,7 +236,7 @@ func BenchmarkServiceEndpoints(b *testing.B) {
|
||||
Namespace: "testing",
|
||||
Name: "foo",
|
||||
Annotations: map[string]string{
|
||||
hostnameAnnotationKey: "foo.example.org",
|
||||
hostnameAnnotationKey: "foo.example.org.",
|
||||
},
|
||||
},
|
||||
Status: v1.ServiceStatus{
|
||||
|
@ -17,6 +17,8 @@ limitations under the License.
|
||||
package source
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
)
|
||||
|
||||
@ -33,3 +35,8 @@ const (
|
||||
type Source interface {
|
||||
Endpoints() ([]*endpoint.Endpoint, error)
|
||||
}
|
||||
|
||||
// sanitizeHostname appends a trailing dot to a hostname if it's missing.
|
||||
func sanitizeHostname(hostname string) string {
|
||||
return strings.Trim(hostname, ".") + "."
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user