mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-06 01:26:59 +02:00
Allow specifying a custom TTL through annotation on Ingress or Service (#320)
* Add RecordTTL * Route53: test for custom TTL * Fix tests * Fix remaining tests * Add ttl when endpoint is created from ingress * Missed a word * Fix bad refactoring * Add ingress custom TTL test * gofmt * Satisfy go-lint * Unshadow `endpoint` in azure provider * Fix and add an output test * Add TTL for endpoints generated from service templates * Take TTL into account when generating update plan * Tests for TTL change impact on the plan * Refactor factory method name * Refactoring * Run gofmt * Make endpoint string format look like BIND config * Update plan and plan_test * Replace NewEndpointWithTTLValue with NewEndpointWithTTL in aws * Remove NewEndpointWithTTLValue func * Update references to TTL * Remove getTTLValue func * Handle merge conflict * Update tests * Update README, CHANGELOG and documentation * Run gofmt * Move getTTLFromAnnotations to a common file * Refactor getTTLFromAnnotations * Gofmt * Add tests for getTTLFromAnnotations * Trigger build * Add boilerplate header * Update README/CHANGELOG according to code review * Add ttl.md and link it from README * change CNAME string to endpoint.RecordTypeCNAME * fix test cases with AWS ALIAS records, these do not behave different in these tests
This commit is contained in:
parent
f5639c4cb7
commit
71723bdd5b
@ -1,4 +1,8 @@
|
||||
- [AWS Route53 provider] Support customization of DNS record TTL through the use of annotation `external-dns.alpha.kubernetes.io/ttl` on services or ingresses (#320) @kevinjqiu
|
||||
- Added support for [DNSimple](https://dnsimple.com/) as DNS provider (#224) @jose5918
|
||||
|
||||
## v0.4.5 - 2017-09-24
|
||||
|
||||
- Add `--log-level` flag to control log verbosity and remove `--debug` flag in favour of `--log-level=debug` (#339) @ultimateboy
|
||||
- AWS: Allow filtering for private and public zones via `--aws-zone-type` flag (#329) @linki
|
||||
- CloudFlare: Add `--cloudflare-proxied` flag to toggle CloudFlare proxy feature (#340) @dunglas
|
||||
|
@ -79,6 +79,14 @@ Annotate the Service with your desired external DNS name. Make sure to change `e
|
||||
$ kubectl annotate service nginx "external-dns.alpha.kubernetes.io/hostname=nginx.example.org."
|
||||
```
|
||||
|
||||
Optionally, you can customize the TTL value of the resulting DNS record by using the `external-dns.alpha.kubernetes.io/ttl` annotation:
|
||||
|
||||
```console
|
||||
$ kubectl annotate service nginx "external-dns.alpha.kubernetes.io/ttl=10"
|
||||
```
|
||||
|
||||
For more details on configuring TTL, see [here](docs/ttl.md).
|
||||
|
||||
Locally run a single sync loop of ExternalDNS.
|
||||
|
||||
```console
|
||||
|
30
docs/ttl.md
Normal file
30
docs/ttl.md
Normal file
@ -0,0 +1,30 @@
|
||||
Configure DNS record TTL (Time-To-Live)
|
||||
=======================================
|
||||
|
||||
An optional annotation `external-dns.alpha.kubernetes.io/ttl` is available to customize the TTL value of a DNS record.
|
||||
|
||||
To configure it, simply annotate a service/ingress, e.g.:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
annotations:
|
||||
external-dns.alpha.kubernetes.io/hostname: nginx.external-dns-test.my-org.com.
|
||||
external-dns.alpha.kubernetes.io/ttl: "60"
|
||||
...
|
||||
```
|
||||
|
||||
TTL must be a positive integer encoded as string.
|
||||
|
||||
Providers
|
||||
=========
|
||||
|
||||
- [x] AWS (Route53)
|
||||
- [ ] Azure
|
||||
- [ ] Cloudflare
|
||||
- [ ] DigitalOcean
|
||||
- [ ] Google
|
||||
- [ ] InMemory
|
||||
|
||||
PRs welcome!
|
@ -184,6 +184,25 @@ $ curl nginx.external-dns-test.my-org.com.
|
||||
|
||||
Ingress objects on AWS require a separately deployed Ingress controller which we'll describe in another tutorial.
|
||||
|
||||
## Custom TTL
|
||||
|
||||
The default DNS record TTL (Time-To-Live) is 300 seconds. You can customize this value by setting the annotation `external-dns.alpha.kubernetes.io/ttl`.
|
||||
e.g., modify the service manifest YAML file above:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: nginx
|
||||
annotations:
|
||||
external-dns.alpha.kubernetes.io/hostname: nginx.external-dns-test.my-org.com.
|
||||
external-dns.alpha.kubernetes.io/ttl: 60
|
||||
spec:
|
||||
...
|
||||
```
|
||||
|
||||
This will set the DNS record's TTL to 60 seconds.
|
||||
|
||||
## Clean up
|
||||
|
||||
Make sure to delete all Service objects before terminating the cluster so all load balancers get cleaned up correctly.
|
||||
|
@ -32,6 +32,14 @@ const (
|
||||
RecordTypeTXT = "TXT"
|
||||
)
|
||||
|
||||
// TTL is a structure defining the TTL of a DNS record
|
||||
type TTL int64
|
||||
|
||||
// IsConfigured returns true if TTL is configured, false otherwise
|
||||
func (ttl TTL) IsConfigured() bool {
|
||||
return ttl > 0
|
||||
}
|
||||
|
||||
// Endpoint is a high-level way of a connection between a service and an IP
|
||||
type Endpoint struct {
|
||||
// The hostname of the DNS record
|
||||
@ -40,17 +48,25 @@ type Endpoint struct {
|
||||
Target string
|
||||
// RecordType type of record, e.g. CNAME, A, TXT etc
|
||||
RecordType string
|
||||
// TTL for the record
|
||||
RecordTTL TTL
|
||||
// Labels stores labels defined for the Endpoint
|
||||
Labels map[string]string
|
||||
}
|
||||
|
||||
// NewEndpoint initialization method to be used to create an endpoint
|
||||
func NewEndpoint(dnsName, target, recordType string) *Endpoint {
|
||||
return NewEndpointWithTTL(dnsName, target, recordType, TTL(0))
|
||||
}
|
||||
|
||||
// NewEndpointWithTTL initialization method to be used to create an endpoint with a TTL struct
|
||||
func NewEndpointWithTTL(dnsName, target, recordType string, ttl TTL) *Endpoint {
|
||||
return &Endpoint{
|
||||
DNSName: strings.TrimSuffix(dnsName, "."),
|
||||
Target: strings.TrimSuffix(target, "."),
|
||||
RecordType: recordType,
|
||||
Labels: map[string]string{},
|
||||
RecordTTL: ttl,
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,5 +80,5 @@ func (e *Endpoint) MergeLabels(labels map[string]string) {
|
||||
}
|
||||
|
||||
func (e *Endpoint) String() string {
|
||||
return fmt.Sprintf(`%s -> %s (type "%s")`, e.DNSName, e.Target, e.RecordType)
|
||||
return fmt.Sprintf("%s %d IN %s %s", e.DNSName, e.RecordTTL, e.RecordType, e.Target)
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ func (b byAllFields) Less(i, j int) bool {
|
||||
// considers example.org. and example.org DNSName/Target as different endpoints
|
||||
func SameEndpoint(a, b *endpoint.Endpoint) bool {
|
||||
return a.DNSName == b.DNSName && a.Target == b.Target && a.RecordType == b.RecordType &&
|
||||
a.Labels[endpoint.OwnerLabelKey] == b.Labels[endpoint.OwnerLabelKey]
|
||||
a.Labels[endpoint.OwnerLabelKey] == b.Labels[endpoint.OwnerLabelKey] && a.RecordTTL == b.RecordTTL
|
||||
}
|
||||
|
||||
// SameEndpoints compares two slices of endpoints regardless of order
|
||||
|
@ -49,15 +49,22 @@ func ExampleSameEndpoints() {
|
||||
Target: "foo.com",
|
||||
RecordType: endpoint.RecordTypeCNAME,
|
||||
},
|
||||
{
|
||||
DNSName: "cbc.com",
|
||||
Target: "foo.com",
|
||||
RecordType: "CNAME",
|
||||
RecordTTL: endpoint.TTL(60),
|
||||
},
|
||||
}
|
||||
sort.Sort(byAllFields(eps))
|
||||
for _, ep := range eps {
|
||||
fmt.Println(ep)
|
||||
}
|
||||
// Output:
|
||||
// abc.com -> 1.2.3.4 (type "A")
|
||||
// abc.com -> something (type "TXT")
|
||||
// bbc.com -> foo.com (type "CNAME")
|
||||
// example.org -> load-balancer.org (type "")
|
||||
// example.org -> load-balancer.org (type "TXT")
|
||||
// abc.com 0 IN A 1.2.3.4
|
||||
// abc.com 0 IN TXT something
|
||||
// bbc.com 0 IN CNAME foo.com
|
||||
// cbc.com 60 IN CNAME foo.com
|
||||
// example.org 0 IN load-balancer.org
|
||||
// example.org 0 IN TXT load-balancer.org
|
||||
}
|
||||
|
34
plan/plan.go
34
plan/plan.go
@ -65,17 +65,26 @@ func (p *Plan) Calculate() *Plan {
|
||||
continue
|
||||
}
|
||||
|
||||
// If there already is a record update it if it changed.
|
||||
if desired.Target != current.Target {
|
||||
changes.UpdateOld = append(changes.UpdateOld, current)
|
||||
targetChanged := targetChanged(desired, current)
|
||||
shouldUpdateTTL := shouldUpdateTTL(desired, current)
|
||||
|
||||
desired.RecordType = current.RecordType // inherit the type from the dns provider
|
||||
desired.MergeLabels(current.Labels) // inherit the labels from the dns provider, including Owner ID
|
||||
changes.UpdateNew = append(changes.UpdateNew, desired)
|
||||
if !targetChanged && !shouldUpdateTTL {
|
||||
log.Debugf("Skipping endpoint %v because nothing has changed", desired)
|
||||
continue
|
||||
}
|
||||
|
||||
log.Debugf("Skipping endpoint %v because target has not changed", desired)
|
||||
changes.UpdateOld = append(changes.UpdateOld, current)
|
||||
desired.MergeLabels(current.Labels) // inherit the labels from the dns provider, including Owner ID
|
||||
|
||||
if targetChanged {
|
||||
desired.RecordType = current.RecordType // inherit the type from the dns provider
|
||||
}
|
||||
|
||||
if !shouldUpdateTTL {
|
||||
desired.RecordTTL = current.RecordTTL
|
||||
}
|
||||
|
||||
changes.UpdateNew = append(changes.UpdateNew, desired)
|
||||
}
|
||||
|
||||
// Ensure all undesired records are removed. Each current record that cannot
|
||||
@ -100,6 +109,17 @@ func (p *Plan) Calculate() *Plan {
|
||||
return plan
|
||||
}
|
||||
|
||||
func targetChanged(desired, current *endpoint.Endpoint) bool {
|
||||
return desired.Target != current.Target
|
||||
}
|
||||
|
||||
func shouldUpdateTTL(desired, current *endpoint.Endpoint) bool {
|
||||
if !desired.RecordTTL.IsConfigured() {
|
||||
return false
|
||||
}
|
||||
return desired.RecordTTL != current.RecordTTL
|
||||
}
|
||||
|
||||
// recordExists checks whether a record can be found in a list of records.
|
||||
func recordExists(needle *endpoint.Endpoint, haystack []*endpoint.Endpoint) (*endpoint.Endpoint, bool) {
|
||||
for _, record := range haystack {
|
||||
|
@ -46,6 +46,16 @@ func TestCalculate(t *testing.T) {
|
||||
typedV2 := []*endpoint.Endpoint{endpoint.NewEndpoint("foo", "v2", endpoint.RecordTypeA)}
|
||||
typedV1 := []*endpoint.Endpoint{endpoint.NewEndpoint("foo", "v1", endpoint.RecordTypeA)}
|
||||
|
||||
// test case with TTL
|
||||
ttl := endpoint.TTL(300)
|
||||
ttl2 := endpoint.TTL(50)
|
||||
ttlV1 := []*endpoint.Endpoint{endpoint.NewEndpointWithTTL("foo", "v1", endpoint.RecordTypeCNAME, ttl)}
|
||||
ttlV2 := []*endpoint.Endpoint{endpoint.NewEndpoint("foo", "v1", endpoint.RecordTypeCNAME)}
|
||||
ttlV3 := []*endpoint.Endpoint{endpoint.NewEndpointWithTTL("foo", "v1", endpoint.RecordTypeCNAME, ttl)}
|
||||
ttlV4 := []*endpoint.Endpoint{endpoint.NewEndpointWithTTL("foo", "v1", endpoint.RecordTypeCNAME, ttl2)}
|
||||
ttlV5 := []*endpoint.Endpoint{endpoint.NewEndpoint("foo", "v2", endpoint.RecordTypeCNAME)}
|
||||
ttlV6 := []*endpoint.Endpoint{endpoint.NewEndpointWithTTL("foo", "v2", endpoint.RecordTypeCNAME, ttl)}
|
||||
|
||||
for _, tc := range []struct {
|
||||
policies []Policy
|
||||
current, desired []*endpoint.Endpoint
|
||||
@ -69,6 +79,14 @@ func TestCalculate(t *testing.T) {
|
||||
{[]Policy{&SyncPolicy{}}, labeledV1, noLabels, empty, labeledV1, labeledV2, empty},
|
||||
// RecordType should be inherited
|
||||
{[]Policy{&SyncPolicy{}}, typedV1, noType, empty, typedV1, typedV2, empty},
|
||||
// If desired TTL is not configured, do not update
|
||||
{[]Policy{&SyncPolicy{}}, ttlV1, ttlV2, empty, empty, empty, empty},
|
||||
// If desired TTL is configured but is the same as current TTL, do not update
|
||||
{[]Policy{&SyncPolicy{}}, ttlV1, ttlV3, empty, empty, empty, empty},
|
||||
// If desired TTL is configured and is not the same as current TTL, need to update
|
||||
{[]Policy{&SyncPolicy{}}, ttlV1, ttlV4, empty, ttlV1, ttlV4, empty},
|
||||
// If target changed and desired TTL is not configured, do not update TTL
|
||||
{[]Policy{&SyncPolicy{}}, ttlV1, ttlV5, empty, ttlV1, ttlV6, empty},
|
||||
} {
|
||||
// setup plan
|
||||
plan := &Plan{
|
||||
|
@ -158,12 +158,18 @@ func (p *AWSProvider) Records() (endpoints []*endpoint.Endpoint, _ error) {
|
||||
if !supportedRecordType(aws.StringValue(r.Type)) {
|
||||
continue
|
||||
}
|
||||
|
||||
var ttl endpoint.TTL
|
||||
if r.TTL != nil {
|
||||
ttl = endpoint.TTL(*r.TTL)
|
||||
}
|
||||
|
||||
for _, rr := range r.ResourceRecords {
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint(wildcardUnescape(aws.StringValue(r.Name)), aws.StringValue(rr.Value), aws.StringValue(r.Type)))
|
||||
endpoints = append(endpoints, endpoint.NewEndpointWithTTL(wildcardUnescape(aws.StringValue(r.Name)), aws.StringValue(rr.Value), aws.StringValue(r.Type), ttl))
|
||||
}
|
||||
|
||||
if r.AliasTarget != nil {
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint(wildcardUnescape(aws.StringValue(r.Name)), aws.StringValue(r.AliasTarget.DNSName), endpoint.RecordTypeCNAME))
|
||||
endpoints = append(endpoints, endpoint.NewEndpointWithTTL(wildcardUnescape(aws.StringValue(r.Name)), aws.StringValue(r.AliasTarget.DNSName), endpoint.RecordTypeCNAME, ttl))
|
||||
}
|
||||
}
|
||||
|
||||
@ -310,7 +316,11 @@ func newChange(action string, endpoint *endpoint.Endpoint) *route53.Change {
|
||||
}
|
||||
} else {
|
||||
change.ResourceRecordSet.Type = aws.String(endpoint.RecordType)
|
||||
change.ResourceRecordSet.TTL = aws.Int64(recordTTL)
|
||||
if !endpoint.RecordTTL.IsConfigured() {
|
||||
change.ResourceRecordSet.TTL = aws.Int64(recordTTL)
|
||||
} else {
|
||||
change.ResourceRecordSet.TTL = aws.Int64(int64(endpoint.RecordTTL))
|
||||
}
|
||||
change.ResourceRecordSet.ResourceRecords = []*route53.ResourceRecord{
|
||||
{
|
||||
Value: aws.String(endpoint.Target),
|
||||
|
@ -203,9 +203,9 @@ func TestAWSZones(t *testing.T) {
|
||||
|
||||
func TestAWSRecords(t *testing.T) {
|
||||
provider := newAWSProvider(t, NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), NewZoneTypeFilter(""), false, []*endpoint.Endpoint{
|
||||
endpoint.NewEndpoint("list-test.zone-1.ext-dns-test-2.teapot.zalan.do", "1.2.3.4", endpoint.RecordTypeA),
|
||||
endpoint.NewEndpoint("list-test.zone-2.ext-dns-test-2.teapot.zalan.do", "8.8.8.8", endpoint.RecordTypeA),
|
||||
endpoint.NewEndpoint("*.wildcard-test.zone-2.ext-dns-test-2.teapot.zalan.do", "8.8.8.8", endpoint.RecordTypeA),
|
||||
endpoint.NewEndpointWithTTL("list-test.zone-1.ext-dns-test-2.teapot.zalan.do", "1.2.3.4", endpoint.RecordTypeA, endpoint.TTL(recordTTL)),
|
||||
endpoint.NewEndpointWithTTL("list-test.zone-2.ext-dns-test-2.teapot.zalan.do", "8.8.8.8", endpoint.RecordTypeA, endpoint.TTL(recordTTL)),
|
||||
endpoint.NewEndpointWithTTL("*.wildcard-test.zone-2.ext-dns-test-2.teapot.zalan.do", "8.8.8.8", endpoint.RecordTypeA, endpoint.TTL(recordTTL)),
|
||||
endpoint.NewEndpoint("list-test-alias.zone-1.ext-dns-test-2.teapot.zalan.do", "foo.eu-central-1.elb.amazonaws.com", endpoint.RecordTypeCNAME),
|
||||
endpoint.NewEndpoint("*.wildcard-test-alias.zone-1.ext-dns-test-2.teapot.zalan.do", "foo.eu-central-1.elb.amazonaws.com", endpoint.RecordTypeCNAME),
|
||||
})
|
||||
@ -214,20 +214,22 @@ func TestAWSRecords(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
validateEndpoints(t, records, []*endpoint.Endpoint{
|
||||
endpoint.NewEndpoint("list-test.zone-1.ext-dns-test-2.teapot.zalan.do", "1.2.3.4", endpoint.RecordTypeA),
|
||||
endpoint.NewEndpoint("list-test.zone-2.ext-dns-test-2.teapot.zalan.do", "8.8.8.8", endpoint.RecordTypeA),
|
||||
endpoint.NewEndpoint("*.wildcard-test.zone-2.ext-dns-test-2.teapot.zalan.do", "8.8.8.8", endpoint.RecordTypeA),
|
||||
endpoint.NewEndpointWithTTL("list-test.zone-1.ext-dns-test-2.teapot.zalan.do", "1.2.3.4", endpoint.RecordTypeA, endpoint.TTL(recordTTL)),
|
||||
endpoint.NewEndpointWithTTL("list-test.zone-2.ext-dns-test-2.teapot.zalan.do", "8.8.8.8", endpoint.RecordTypeA, endpoint.TTL(recordTTL)),
|
||||
endpoint.NewEndpointWithTTL("*.wildcard-test.zone-2.ext-dns-test-2.teapot.zalan.do", "8.8.8.8", endpoint.RecordTypeA, endpoint.TTL(recordTTL)),
|
||||
endpoint.NewEndpoint("list-test-alias.zone-1.ext-dns-test-2.teapot.zalan.do", "foo.eu-central-1.elb.amazonaws.com", endpoint.RecordTypeCNAME),
|
||||
endpoint.NewEndpoint("*.wildcard-test-alias.zone-1.ext-dns-test-2.teapot.zalan.do", "foo.eu-central-1.elb.amazonaws.com", endpoint.RecordTypeCNAME),
|
||||
})
|
||||
}
|
||||
|
||||
func TestAWSCreateRecords(t *testing.T) {
|
||||
customTTL := endpoint.TTL(60)
|
||||
provider := newAWSProvider(t, NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), NewZoneTypeFilter(""), false, []*endpoint.Endpoint{})
|
||||
|
||||
records := []*endpoint.Endpoint{
|
||||
endpoint.NewEndpoint("create-test.zone-1.ext-dns-test-2.teapot.zalan.do", "1.2.3.4", endpoint.RecordTypeA),
|
||||
endpoint.NewEndpoint("create-test.zone-2.ext-dns-test-2.teapot.zalan.do", "8.8.8.8", endpoint.RecordTypeA),
|
||||
endpoint.NewEndpointWithTTL("create-test-cname-custom-ttl.zone-1.ext-dns-test-2.teapot.zalan.do", "172.17.0.1", endpoint.RecordTypeA, customTTL),
|
||||
endpoint.NewEndpoint("create-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", "foo.elb.amazonaws.com", endpoint.RecordTypeCNAME),
|
||||
}
|
||||
|
||||
@ -237,17 +239,18 @@ func TestAWSCreateRecords(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
validateEndpoints(t, records, []*endpoint.Endpoint{
|
||||
endpoint.NewEndpoint("create-test.zone-1.ext-dns-test-2.teapot.zalan.do", "1.2.3.4", endpoint.RecordTypeA),
|
||||
endpoint.NewEndpoint("create-test.zone-2.ext-dns-test-2.teapot.zalan.do", "8.8.8.8", endpoint.RecordTypeA),
|
||||
endpoint.NewEndpoint("create-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", "foo.elb.amazonaws.com", endpoint.RecordTypeCNAME),
|
||||
endpoint.NewEndpointWithTTL("create-test.zone-1.ext-dns-test-2.teapot.zalan.do", "1.2.3.4", endpoint.RecordTypeA, endpoint.TTL(recordTTL)),
|
||||
endpoint.NewEndpointWithTTL("create-test.zone-2.ext-dns-test-2.teapot.zalan.do", "8.8.8.8", endpoint.RecordTypeA, endpoint.TTL(recordTTL)),
|
||||
endpoint.NewEndpointWithTTL("create-test-cname-custom-ttl.zone-1.ext-dns-test-2.teapot.zalan.do", "172.17.0.1", endpoint.RecordTypeA, customTTL),
|
||||
endpoint.NewEndpointWithTTL("create-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", "foo.elb.amazonaws.com", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL)),
|
||||
})
|
||||
}
|
||||
|
||||
func TestAWSUpdateRecords(t *testing.T) {
|
||||
provider := newAWSProvider(t, NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), NewZoneTypeFilter(""), false, []*endpoint.Endpoint{
|
||||
endpoint.NewEndpoint("update-test.zone-1.ext-dns-test-2.teapot.zalan.do", "8.8.8.8", endpoint.RecordTypeA),
|
||||
endpoint.NewEndpoint("update-test.zone-2.ext-dns-test-2.teapot.zalan.do", "8.8.4.4", endpoint.RecordTypeA),
|
||||
endpoint.NewEndpoint("update-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", "foo.elb.amazonaws.com", endpoint.RecordTypeCNAME),
|
||||
endpoint.NewEndpointWithTTL("update-test.zone-1.ext-dns-test-2.teapot.zalan.do", "8.8.8.8", endpoint.RecordTypeA, endpoint.TTL(recordTTL)),
|
||||
endpoint.NewEndpointWithTTL("update-test.zone-2.ext-dns-test-2.teapot.zalan.do", "8.8.4.4", endpoint.RecordTypeA, endpoint.TTL(recordTTL)),
|
||||
endpoint.NewEndpointWithTTL("update-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", "foo.elb.amazonaws.com", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL)),
|
||||
})
|
||||
|
||||
currentRecords := []*endpoint.Endpoint{
|
||||
@ -267,17 +270,17 @@ func TestAWSUpdateRecords(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
validateEndpoints(t, records, []*endpoint.Endpoint{
|
||||
endpoint.NewEndpoint("update-test.zone-1.ext-dns-test-2.teapot.zalan.do", "1.2.3.4", endpoint.RecordTypeA),
|
||||
endpoint.NewEndpoint("update-test.zone-2.ext-dns-test-2.teapot.zalan.do", "4.3.2.1", endpoint.RecordTypeA),
|
||||
endpoint.NewEndpoint("update-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", "bar.elb.amazonaws.com", endpoint.RecordTypeCNAME),
|
||||
endpoint.NewEndpointWithTTL("update-test.zone-1.ext-dns-test-2.teapot.zalan.do", "1.2.3.4", endpoint.RecordTypeA, endpoint.TTL(recordTTL)),
|
||||
endpoint.NewEndpointWithTTL("update-test.zone-2.ext-dns-test-2.teapot.zalan.do", "4.3.2.1", endpoint.RecordTypeA, endpoint.TTL(recordTTL)),
|
||||
endpoint.NewEndpointWithTTL("update-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", "bar.elb.amazonaws.com", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL)),
|
||||
})
|
||||
}
|
||||
|
||||
func TestAWSDeleteRecords(t *testing.T) {
|
||||
originalEndpoints := []*endpoint.Endpoint{
|
||||
endpoint.NewEndpoint("delete-test.zone-1.ext-dns-test-2.teapot.zalan.do", "1.2.3.4", endpoint.RecordTypeA),
|
||||
endpoint.NewEndpoint("delete-test.zone-2.ext-dns-test-2.teapot.zalan.do", "8.8.8.8", endpoint.RecordTypeA),
|
||||
endpoint.NewEndpoint("delete-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", "baz.elb.amazonaws.com", endpoint.RecordTypeCNAME),
|
||||
endpoint.NewEndpointWithTTL("delete-test.zone-1.ext-dns-test-2.teapot.zalan.do", "1.2.3.4", endpoint.RecordTypeA, endpoint.TTL(recordTTL)),
|
||||
endpoint.NewEndpointWithTTL("delete-test.zone-2.ext-dns-test-2.teapot.zalan.do", "8.8.8.8", endpoint.RecordTypeA, endpoint.TTL(recordTTL)),
|
||||
endpoint.NewEndpointWithTTL("delete-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", "baz.elb.amazonaws.com", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL)),
|
||||
endpoint.NewEndpoint("delete-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", "foo.eu-central-1.elb.amazonaws.com", endpoint.RecordTypeCNAME),
|
||||
endpoint.NewEndpoint("delete-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do", "foo.eu-central-1.elb.amazonaws.com", endpoint.RecordTypeCNAME),
|
||||
}
|
||||
@ -295,14 +298,14 @@ func TestAWSDeleteRecords(t *testing.T) {
|
||||
|
||||
func TestAWSApplyChanges(t *testing.T) {
|
||||
provider := newAWSProvider(t, NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), NewZoneTypeFilter(""), false, []*endpoint.Endpoint{
|
||||
endpoint.NewEndpoint("update-test.zone-1.ext-dns-test-2.teapot.zalan.do", "8.8.8.8", endpoint.RecordTypeA),
|
||||
endpoint.NewEndpoint("delete-test.zone-1.ext-dns-test-2.teapot.zalan.do", "8.8.8.8", endpoint.RecordTypeA),
|
||||
endpoint.NewEndpoint("update-test.zone-2.ext-dns-test-2.teapot.zalan.do", "8.8.4.4", endpoint.RecordTypeA),
|
||||
endpoint.NewEndpoint("delete-test.zone-2.ext-dns-test-2.teapot.zalan.do", "8.8.4.4", endpoint.RecordTypeA),
|
||||
endpoint.NewEndpoint("update-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", "bar.elb.amazonaws.com", endpoint.RecordTypeCNAME),
|
||||
endpoint.NewEndpoint("delete-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", "qux.elb.amazonaws.com", endpoint.RecordTypeCNAME),
|
||||
endpoint.NewEndpoint("update-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do", "bar.elb.amazonaws.com", endpoint.RecordTypeCNAME),
|
||||
endpoint.NewEndpoint("delete-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do", "qux.elb.amazonaws.com", endpoint.RecordTypeCNAME),
|
||||
endpoint.NewEndpointWithTTL("update-test.zone-1.ext-dns-test-2.teapot.zalan.do", "8.8.8.8", endpoint.RecordTypeA, endpoint.TTL(recordTTL)),
|
||||
endpoint.NewEndpointWithTTL("delete-test.zone-1.ext-dns-test-2.teapot.zalan.do", "8.8.8.8", endpoint.RecordTypeA, endpoint.TTL(recordTTL)),
|
||||
endpoint.NewEndpointWithTTL("update-test.zone-2.ext-dns-test-2.teapot.zalan.do", "8.8.4.4", endpoint.RecordTypeA, endpoint.TTL(recordTTL)),
|
||||
endpoint.NewEndpointWithTTL("delete-test.zone-2.ext-dns-test-2.teapot.zalan.do", "8.8.4.4", endpoint.RecordTypeA, endpoint.TTL(recordTTL)),
|
||||
endpoint.NewEndpointWithTTL("update-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", "bar.elb.amazonaws.com", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL)),
|
||||
endpoint.NewEndpointWithTTL("delete-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", "qux.elb.amazonaws.com", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL)),
|
||||
endpoint.NewEndpointWithTTL("update-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do", "bar.elb.amazonaws.com", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL)),
|
||||
endpoint.NewEndpointWithTTL("delete-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do", "qux.elb.amazonaws.com", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL)),
|
||||
})
|
||||
|
||||
createRecords := []*endpoint.Endpoint{
|
||||
@ -345,27 +348,27 @@ func TestAWSApplyChanges(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
validateEndpoints(t, records, []*endpoint.Endpoint{
|
||||
endpoint.NewEndpoint("create-test.zone-1.ext-dns-test-2.teapot.zalan.do", "8.8.8.8", endpoint.RecordTypeA),
|
||||
endpoint.NewEndpoint("update-test.zone-1.ext-dns-test-2.teapot.zalan.do", "1.2.3.4", endpoint.RecordTypeA),
|
||||
endpoint.NewEndpoint("create-test.zone-2.ext-dns-test-2.teapot.zalan.do", "8.8.4.4", endpoint.RecordTypeA),
|
||||
endpoint.NewEndpoint("update-test.zone-2.ext-dns-test-2.teapot.zalan.do", "4.3.2.1", endpoint.RecordTypeA),
|
||||
endpoint.NewEndpoint("create-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", "foo.elb.amazonaws.com", endpoint.RecordTypeCNAME),
|
||||
endpoint.NewEndpoint("update-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", "baz.elb.amazonaws.com", endpoint.RecordTypeCNAME),
|
||||
endpoint.NewEndpoint("create-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do", "foo.elb.amazonaws.com", endpoint.RecordTypeCNAME),
|
||||
endpoint.NewEndpoint("update-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do", "baz.elb.amazonaws.com", endpoint.RecordTypeCNAME),
|
||||
endpoint.NewEndpointWithTTL("create-test.zone-1.ext-dns-test-2.teapot.zalan.do", "8.8.8.8", endpoint.RecordTypeA, endpoint.TTL(recordTTL)),
|
||||
endpoint.NewEndpointWithTTL("update-test.zone-1.ext-dns-test-2.teapot.zalan.do", "1.2.3.4", endpoint.RecordTypeA, endpoint.TTL(recordTTL)),
|
||||
endpoint.NewEndpointWithTTL("create-test.zone-2.ext-dns-test-2.teapot.zalan.do", "8.8.4.4", endpoint.RecordTypeA, endpoint.TTL(recordTTL)),
|
||||
endpoint.NewEndpointWithTTL("update-test.zone-2.ext-dns-test-2.teapot.zalan.do", "4.3.2.1", endpoint.RecordTypeA, endpoint.TTL(recordTTL)),
|
||||
endpoint.NewEndpointWithTTL("create-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", "foo.elb.amazonaws.com", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL)),
|
||||
endpoint.NewEndpointWithTTL("update-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", "baz.elb.amazonaws.com", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL)),
|
||||
endpoint.NewEndpointWithTTL("create-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do", "foo.elb.amazonaws.com", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL)),
|
||||
endpoint.NewEndpointWithTTL("update-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do", "baz.elb.amazonaws.com", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL)),
|
||||
})
|
||||
}
|
||||
|
||||
func TestAWSApplyChangesDryRun(t *testing.T) {
|
||||
originalEndpoints := []*endpoint.Endpoint{
|
||||
endpoint.NewEndpoint("update-test.zone-1.ext-dns-test-2.teapot.zalan.do", "8.8.8.8", endpoint.RecordTypeA),
|
||||
endpoint.NewEndpoint("delete-test.zone-1.ext-dns-test-2.teapot.zalan.do", "8.8.8.8", endpoint.RecordTypeA),
|
||||
endpoint.NewEndpoint("update-test.zone-2.ext-dns-test-2.teapot.zalan.do", "8.8.4.4", endpoint.RecordTypeA),
|
||||
endpoint.NewEndpoint("delete-test.zone-2.ext-dns-test-2.teapot.zalan.do", "8.8.4.4", endpoint.RecordTypeA),
|
||||
endpoint.NewEndpoint("update-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", "bar.elb.amazonaws.com", endpoint.RecordTypeCNAME),
|
||||
endpoint.NewEndpoint("delete-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", "qux.elb.amazonaws.com", endpoint.RecordTypeCNAME),
|
||||
endpoint.NewEndpoint("update-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do", "bar.elb.amazonaws.com", endpoint.RecordTypeCNAME),
|
||||
endpoint.NewEndpoint("delete-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do", "qux.elb.amazonaws.com", endpoint.RecordTypeCNAME),
|
||||
endpoint.NewEndpointWithTTL("update-test.zone-1.ext-dns-test-2.teapot.zalan.do", "8.8.8.8", endpoint.RecordTypeA, endpoint.TTL(recordTTL)),
|
||||
endpoint.NewEndpointWithTTL("delete-test.zone-1.ext-dns-test-2.teapot.zalan.do", "8.8.8.8", endpoint.RecordTypeA, endpoint.TTL(recordTTL)),
|
||||
endpoint.NewEndpointWithTTL("update-test.zone-2.ext-dns-test-2.teapot.zalan.do", "8.8.4.4", endpoint.RecordTypeA, endpoint.TTL(recordTTL)),
|
||||
endpoint.NewEndpointWithTTL("delete-test.zone-2.ext-dns-test-2.teapot.zalan.do", "8.8.4.4", endpoint.RecordTypeA, endpoint.TTL(recordTTL)),
|
||||
endpoint.NewEndpointWithTTL("update-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", "bar.elb.amazonaws.com", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL)),
|
||||
endpoint.NewEndpointWithTTL("delete-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", "qux.elb.amazonaws.com", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL)),
|
||||
endpoint.NewEndpointWithTTL("update-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do", "bar.elb.amazonaws.com", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL)),
|
||||
endpoint.NewEndpointWithTTL("delete-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do", "qux.elb.amazonaws.com", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL)),
|
||||
}
|
||||
|
||||
provider := newAWSProvider(t, NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), NewZoneTypeFilter(""), true, originalEndpoints)
|
||||
@ -490,7 +493,7 @@ func TestAWSChangesByZones(t *testing.T) {
|
||||
}
|
||||
|
||||
func validateEndpoints(t *testing.T, endpoints []*endpoint.Endpoint, expected []*endpoint.Endpoint) {
|
||||
assert.True(t, testutils.SameEndpoints(endpoints, expected), "expected and actual endpoints don't match")
|
||||
assert.True(t, testutils.SameEndpoints(endpoints, expected), "expected and actual endpoints don't match. %s:%s", endpoints, expected)
|
||||
}
|
||||
|
||||
func validateAWSZones(t *testing.T, zones map[string]*route53.HostedZone, expected map[string]*route53.HostedZone) {
|
||||
|
@ -151,14 +151,14 @@ func (p *AzureProvider) Records() (endpoints []*endpoint.Endpoint, _ error) {
|
||||
log.Errorf("Failed to extract target for '%s' with type '%s'.", name, recordType)
|
||||
return true
|
||||
}
|
||||
endpoint := endpoint.NewEndpoint(name, target, recordType)
|
||||
ep := endpoint.NewEndpoint(name, target, recordType)
|
||||
log.Debugf(
|
||||
"Found %s record for '%s' with target '%s'.",
|
||||
endpoint.RecordType,
|
||||
endpoint.DNSName,
|
||||
endpoint.Target,
|
||||
ep.RecordType,
|
||||
ep.DNSName,
|
||||
ep.Target,
|
||||
)
|
||||
endpoints = append(endpoints, endpoint)
|
||||
endpoints = append(endpoints, ep)
|
||||
return true
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -112,11 +112,15 @@ func getEndpointsFromTargetAnnotation(ing *v1beta1.Ingress, hostname string) []*
|
||||
// Get the desired hostname of the ingress from the annotation.
|
||||
targetAnnotation, exists := ing.Annotations[targetAnnotationKey]
|
||||
if exists {
|
||||
ttl, err := getTTLFromAnnotations(ing.Annotations)
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
}
|
||||
// splits the hostname annotation and removes the trailing periods
|
||||
targetsList := strings.Split(strings.Replace(targetAnnotation, " ", "", -1), ",")
|
||||
for _, targetHostname := range targetsList {
|
||||
targetHostname = strings.TrimSuffix(targetHostname, ".")
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint(hostname, targetHostname, suitableType(targetHostname)))
|
||||
endpoints = append(endpoints, endpoint.NewEndpointWithTTL(hostname, targetHostname, suitableType(targetHostname), ttl))
|
||||
}
|
||||
}
|
||||
return endpoints
|
||||
@ -139,12 +143,16 @@ func (sc *ingressSource) endpointsFromTemplate(ing *v1beta1.Ingress) ([]*endpoin
|
||||
return endpoints, nil
|
||||
}
|
||||
|
||||
ttl, err := getTTLFromAnnotations(ing.Annotations)
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
}
|
||||
for _, lb := range ing.Status.LoadBalancer.Ingress {
|
||||
if lb.IP != "" {
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint(hostname, lb.IP, endpoint.RecordTypeA))
|
||||
endpoints = append(endpoints, endpoint.NewEndpointWithTTL(hostname, lb.IP, endpoint.RecordTypeA, ttl))
|
||||
}
|
||||
if lb.Hostname != "" {
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint(hostname, lb.Hostname, endpoint.RecordTypeCNAME))
|
||||
endpoints = append(endpoints, endpoint.NewEndpointWithTTL(hostname, lb.Hostname, endpoint.RecordTypeCNAME, ttl))
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,12 +175,17 @@ func endpointsFromIngress(ing *v1beta1.Ingress) []*endpoint.Endpoint {
|
||||
continue
|
||||
}
|
||||
|
||||
ttl, err := getTTLFromAnnotations(ing.Annotations)
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
}
|
||||
|
||||
for _, lb := range ing.Status.LoadBalancer.Ingress {
|
||||
if lb.IP != "" {
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint(rule.Host, lb.IP, endpoint.RecordTypeA))
|
||||
endpoints = append(endpoints, endpoint.NewEndpointWithTTL(rule.Host, lb.IP, endpoint.RecordTypeA, ttl))
|
||||
}
|
||||
if lb.Hostname != "" {
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint(rule.Host, lb.Hostname, endpoint.RecordTypeCNAME))
|
||||
endpoints = append(endpoints, endpoint.NewEndpointWithTTL(rule.Host, lb.Hostname, endpoint.RecordTypeCNAME, ttl))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -388,6 +388,44 @@ func testIngressEndpoints(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "ingress rules with annotation and custom TTL",
|
||||
targetNamespace: "",
|
||||
ingressItems: []fakeIngress{
|
||||
{
|
||||
name: "fake1",
|
||||
namespace: namespace,
|
||||
annotations: map[string]string{
|
||||
targetAnnotationKey: "ingress-target.com",
|
||||
ttlAnnotationKey: "6",
|
||||
},
|
||||
dnsnames: []string{"example.org"},
|
||||
ips: []string{},
|
||||
},
|
||||
{
|
||||
name: "fake2",
|
||||
namespace: namespace,
|
||||
annotations: map[string]string{
|
||||
targetAnnotationKey: "ingress-target.com",
|
||||
ttlAnnotationKey: "1",
|
||||
},
|
||||
dnsnames: []string{"example2.org"},
|
||||
ips: []string{"8.8.8.8"},
|
||||
},
|
||||
},
|
||||
expected: []*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "example.org",
|
||||
Target: "ingress-target.com",
|
||||
RecordTTL: endpoint.TTL(6),
|
||||
},
|
||||
{
|
||||
DNSName: "example2.org",
|
||||
Target: "ingress-target.com",
|
||||
RecordTTL: endpoint.TTL(1),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "template for ingress with annotation",
|
||||
targetNamespace: "",
|
||||
|
@ -160,30 +160,37 @@ func (sc *serviceSource) generateEndpoints(svc *v1.Service, hostname string) []*
|
||||
endpoints = append(endpoints, extractServiceIps(svc, hostname)...)
|
||||
}
|
||||
}
|
||||
|
||||
return endpoints
|
||||
}
|
||||
|
||||
func extractServiceIps(svc *v1.Service, hostname string) []*endpoint.Endpoint {
|
||||
ttl, err := getTTLFromAnnotations(svc.Annotations)
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
}
|
||||
if svc.Spec.ClusterIP == v1.ClusterIPNone {
|
||||
log.Debugf("Unable to associate %s headless service with a Cluster IP", svc.Name)
|
||||
return []*endpoint.Endpoint{}
|
||||
}
|
||||
|
||||
return []*endpoint.Endpoint{endpoint.NewEndpoint(hostname, svc.Spec.ClusterIP, endpoint.RecordTypeA)}
|
||||
return []*endpoint.Endpoint{endpoint.NewEndpointWithTTL(hostname, svc.Spec.ClusterIP, endpoint.RecordTypeA, ttl)}
|
||||
}
|
||||
|
||||
func extractLoadBalancerEndpoints(svc *v1.Service, hostname string) []*endpoint.Endpoint {
|
||||
var endpoints []*endpoint.Endpoint
|
||||
|
||||
ttl, err := getTTLFromAnnotations(svc.Annotations)
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
}
|
||||
// Create a corresponding endpoint for each configured external entrypoint.
|
||||
for _, lb := range svc.Status.LoadBalancer.Ingress {
|
||||
if lb.IP != "" {
|
||||
//TODO(ideahitme): consider retrieving record type from resource annotation instead of empty
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint(hostname, lb.IP, endpoint.RecordTypeA))
|
||||
endpoints = append(endpoints, endpoint.NewEndpointWithTTL(hostname, lb.IP, endpoint.RecordTypeA, ttl))
|
||||
}
|
||||
if lb.Hostname != "" {
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint(hostname, lb.Hostname, endpoint.RecordTypeCNAME))
|
||||
endpoints = append(endpoints, endpoint.NewEndpointWithTTL(hostname, lb.Hostname, endpoint.RecordTypeCNAME, ttl))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -488,6 +488,85 @@ func testServiceSourceEndpoints(t *testing.T) {
|
||||
[]*endpoint.Endpoint{},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"ttl not annotated should have RecordTTL.IsConfigured set to false",
|
||||
"",
|
||||
"testing",
|
||||
"foo",
|
||||
v1.ServiceTypeLoadBalancer,
|
||||
"",
|
||||
"",
|
||||
map[string]string{},
|
||||
map[string]string{
|
||||
hostnameAnnotationKey: "foo.example.org.",
|
||||
},
|
||||
"",
|
||||
[]string{"1.2.3.4"},
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "foo.example.org", Target: "1.2.3.4", RecordTTL: endpoint.TTL(0)},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"ttl annotated but invalid should have RecordTTL.IsConfigured set to false",
|
||||
"",
|
||||
"testing",
|
||||
"foo",
|
||||
v1.ServiceTypeLoadBalancer,
|
||||
"",
|
||||
"",
|
||||
map[string]string{},
|
||||
map[string]string{
|
||||
hostnameAnnotationKey: "foo.example.org.",
|
||||
ttlAnnotationKey: "foo",
|
||||
},
|
||||
"",
|
||||
[]string{"1.2.3.4"},
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "foo.example.org", Target: "1.2.3.4", RecordTTL: endpoint.TTL(0)},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"ttl annotated and is valid should set Record.TTL",
|
||||
"",
|
||||
"testing",
|
||||
"foo",
|
||||
v1.ServiceTypeLoadBalancer,
|
||||
"",
|
||||
"",
|
||||
map[string]string{},
|
||||
map[string]string{
|
||||
hostnameAnnotationKey: "foo.example.org.",
|
||||
ttlAnnotationKey: "10",
|
||||
},
|
||||
"",
|
||||
[]string{"1.2.3.4"},
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "foo.example.org", Target: "1.2.3.4", RecordTTL: endpoint.TTL(10)},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"Negative ttl is not valid",
|
||||
"",
|
||||
"testing",
|
||||
"foo",
|
||||
v1.ServiceTypeLoadBalancer,
|
||||
"",
|
||||
"",
|
||||
map[string]string{},
|
||||
map[string]string{
|
||||
hostnameAnnotationKey: "foo.example.org.",
|
||||
ttlAnnotationKey: "-10",
|
||||
},
|
||||
"",
|
||||
[]string{"1.2.3.4"},
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "foo.example.org", Target: "1.2.3.4", RecordTTL: endpoint.TTL(0)},
|
||||
},
|
||||
false,
|
||||
},
|
||||
} {
|
||||
t.Run(tc.title, func(t *testing.T) {
|
||||
// Create a Kubernetes testing client
|
||||
|
@ -43,6 +43,10 @@ func validateEndpoint(t *testing.T, endpoint, expected *endpoint.Endpoint) {
|
||||
t.Errorf("expected %s, got %s", expected.Target, endpoint.Target)
|
||||
}
|
||||
|
||||
if endpoint.RecordTTL != expected.RecordTTL {
|
||||
t.Errorf("expected %v, got %v", expected.RecordTTL, endpoint.RecordTTL)
|
||||
}
|
||||
|
||||
// if non-empty record type is expected, check that it matches.
|
||||
if expected.RecordType != "" && endpoint.RecordType != expected.RecordType {
|
||||
t.Errorf("expected %s, got %s", expected.RecordType, endpoint.RecordType)
|
||||
|
@ -17,7 +17,10 @@ limitations under the License.
|
||||
package source
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
)
|
||||
@ -29,15 +32,38 @@ const (
|
||||
hostnameAnnotationKey = "external-dns.alpha.kubernetes.io/hostname"
|
||||
// The annotation used for defining the desired ingress target
|
||||
targetAnnotationKey = "external-dns.alpha.kubernetes.io/target"
|
||||
// The value of the controller annotation so that we feel resposible
|
||||
// The annotation used for defining the desired DNS record TTL
|
||||
ttlAnnotationKey = "external-dns.alpha.kubernetes.io/ttl"
|
||||
// The value of the controller annotation so that we feel responsible
|
||||
controllerAnnotationValue = "dns-controller"
|
||||
)
|
||||
|
||||
const (
|
||||
ttlMinimum = 1
|
||||
ttlMaximum = math.MaxUint32
|
||||
)
|
||||
|
||||
// Source defines the interface Endpoint sources should implement.
|
||||
type Source interface {
|
||||
Endpoints() ([]*endpoint.Endpoint, error)
|
||||
}
|
||||
|
||||
func getTTLFromAnnotations(annotations map[string]string) (endpoint.TTL, error) {
|
||||
ttlNotConfigured := endpoint.TTL(0)
|
||||
ttlAnnotation, exists := annotations[ttlAnnotationKey]
|
||||
if !exists {
|
||||
return ttlNotConfigured, nil
|
||||
}
|
||||
ttlValue, err := strconv.ParseInt(ttlAnnotation, 10, 64)
|
||||
if err != nil {
|
||||
return ttlNotConfigured, fmt.Errorf("\"%v\" is not a valid TTL value", ttlAnnotation)
|
||||
}
|
||||
if ttlValue < ttlMinimum || ttlValue > ttlMaximum {
|
||||
return ttlNotConfigured, fmt.Errorf("TTL value must be between [%d, %d]", ttlMinimum, ttlMaximum)
|
||||
}
|
||||
return endpoint.TTL(ttlValue), nil
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
@ -16,7 +16,65 @@ limitations under the License.
|
||||
|
||||
package source
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetTTLFromAnnotations(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
title string
|
||||
annotations map[string]string
|
||||
expectedTTL endpoint.TTL
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
title: "TTL annotation not present",
|
||||
annotations: map[string]string{"foo": "bar"},
|
||||
expectedTTL: endpoint.TTL(0),
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
title: "TTL annotation value is not a number",
|
||||
annotations: map[string]string{ttlAnnotationKey: "foo"},
|
||||
expectedTTL: endpoint.TTL(0),
|
||||
expectedErr: fmt.Errorf("\"foo\" is not a valid TTL value"),
|
||||
},
|
||||
{
|
||||
title: "TTL annotation value is empty",
|
||||
annotations: map[string]string{ttlAnnotationKey: ""},
|
||||
expectedTTL: endpoint.TTL(0),
|
||||
expectedErr: fmt.Errorf("\"\" is not a valid TTL value"),
|
||||
},
|
||||
{
|
||||
title: "TTL annotation value is negative number",
|
||||
annotations: map[string]string{ttlAnnotationKey: "-1"},
|
||||
expectedTTL: endpoint.TTL(0),
|
||||
expectedErr: fmt.Errorf("TTL value must be between [%d, %d]", ttlMinimum, ttlMaximum),
|
||||
},
|
||||
{
|
||||
title: "TTL annotation value is too high",
|
||||
annotations: map[string]string{ttlAnnotationKey: fmt.Sprintf("%d", 1<<32)},
|
||||
expectedTTL: endpoint.TTL(0),
|
||||
expectedErr: fmt.Errorf("TTL value must be between [%d, %d]", ttlMinimum, ttlMaximum),
|
||||
},
|
||||
{
|
||||
title: "TTL annotation value is set correctly",
|
||||
annotations: map[string]string{ttlAnnotationKey: "60"},
|
||||
expectedTTL: endpoint.TTL(60),
|
||||
expectedErr: nil,
|
||||
},
|
||||
} {
|
||||
t.Run(tc.title, func(t *testing.T) {
|
||||
ttl, err := getTTLFromAnnotations(tc.annotations)
|
||||
assert.Equal(t, tc.expectedTTL, ttl)
|
||||
assert.Equal(t, tc.expectedErr, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSuitableType(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
|
Loading…
Reference in New Issue
Block a user