mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-06 01:26:59 +02:00
kickoff txt registry (#137)
* kickoff txt registry * fix inmemory dns provider to include recordtype info for validation * Merge master * fix ununsed variable in inmemory provider * add tests for records * add test for no prefix name formatter * implement apply changes with tests * add flag to enable txt registry * add txt registry to main * improve sort testing * filter out non-owned records * NewEndpoint(...) requires record type * use newendpoint in aws_test, fix tests * change suitable type implementation * fix the test for compatibility component * change inmemory provider to include recordtype and use suitable type * fix comments, CNAME should target hostname * name mapper do not use pointer on struct * txt prefix - just concatenate, remove spew, fix txt record label * allow TXT records as result from dns provider * add changelog * fix tests * TXT records need to be enclosed in double quotes
This commit is contained in:
parent
3d296f37d9
commit
98de0142ba
2
.gitignore
vendored
2
.gitignore
vendored
@ -20,6 +20,7 @@
|
||||
/output*/
|
||||
/_output*/
|
||||
/_output
|
||||
/build
|
||||
|
||||
# Emacs save files
|
||||
*~
|
||||
@ -40,3 +41,4 @@ cscope.*
|
||||
|
||||
# coverage output
|
||||
cover.out
|
||||
*.coverprofile
|
||||
|
@ -1,3 +1,9 @@
|
||||
Features:
|
||||
- Ownership via TXT records
|
||||
1. Create TXT records to mark the records managed by External DNS
|
||||
2. Supported for AWS Route53 and Google CloudDNS
|
||||
3. Configurable TXT record DNS name format
|
||||
|
||||
## v0.2.0 - 2017-04-07
|
||||
|
||||
Features:
|
||||
|
@ -31,16 +31,19 @@ type Endpoint struct {
|
||||
DNSName string
|
||||
// The target the DNS record points to
|
||||
Target string
|
||||
// RecordType type of record, e.g. CNAME, A, TXT etc
|
||||
RecordType string
|
||||
// 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 string) *Endpoint {
|
||||
func NewEndpoint(dnsName, target, recordType string) *Endpoint {
|
||||
return &Endpoint{
|
||||
DNSName: strings.TrimSuffix(dnsName, "."),
|
||||
Target: strings.TrimSuffix(target, "."),
|
||||
Labels: map[string]string{},
|
||||
DNSName: strings.TrimSuffix(dnsName, "."),
|
||||
Target: strings.TrimSuffix(target, "."),
|
||||
RecordType: recordType,
|
||||
Labels: map[string]string{},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,22 +22,22 @@ import (
|
||||
)
|
||||
|
||||
func TestNewEndpoint(t *testing.T) {
|
||||
e := NewEndpoint("example.org", "1.2.3.4")
|
||||
if e.DNSName != "example.org" || e.Target != "1.2.3.4" {
|
||||
e := NewEndpoint("example.org", "foo.com", "CNAME")
|
||||
if e.DNSName != "example.org" || e.Target != "foo.com" || e.RecordType != "CNAME" {
|
||||
t.Error("endpoint is not initialized correctly")
|
||||
}
|
||||
if e.Labels == nil {
|
||||
t.Error("Labels is not initialized")
|
||||
}
|
||||
|
||||
w := NewEndpoint("example.org.", "load-balancer.com.")
|
||||
if w.DNSName != "example.org" || w.Target != "load-balancer.com" {
|
||||
w := NewEndpoint("example.org.", "load-balancer.com.", "")
|
||||
if w.DNSName != "example.org" || w.Target != "load-balancer.com" || w.RecordType != "" {
|
||||
t.Error("endpoint is not initialized correctly")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeLabels(t *testing.T) {
|
||||
e := NewEndpoint("abc.com", "1.2.3.4")
|
||||
e := NewEndpoint("abc.com", "1.2.3.4", "A")
|
||||
e.Labels = map[string]string{
|
||||
"foo": "bar",
|
||||
"baz": "qux",
|
||||
|
@ -17,14 +17,35 @@ limitations under the License.
|
||||
package testutils
|
||||
|
||||
import "github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
import "sort"
|
||||
|
||||
/** test utility functions for endpoints verifications */
|
||||
|
||||
// SameEndpoint returns true if two endpoint are same
|
||||
type byAllFields []*endpoint.Endpoint
|
||||
|
||||
func (b byAllFields) Len() int { return len(b) }
|
||||
func (b byAllFields) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
|
||||
func (b byAllFields) Less(i, j int) bool {
|
||||
if b[i].DNSName < b[j].DNSName {
|
||||
return true
|
||||
}
|
||||
if b[i].DNSName == b[j].DNSName {
|
||||
if b[i].Target < b[j].Target {
|
||||
return true
|
||||
}
|
||||
if b[i].Target == b[j].Target {
|
||||
return b[i].RecordType <= b[j].RecordType
|
||||
}
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SameEndpoint returns true if two endpoints are same
|
||||
// considers example.org. and example.org DNSName/Target as different endpoints
|
||||
// TODO:might need reconsideration regarding trailing dot
|
||||
func SameEndpoint(a, b *endpoint.Endpoint) bool {
|
||||
return a.DNSName == b.DNSName && a.Target == b.Target && a.Labels[endpoint.OwnerLabelKey] == b.Labels[endpoint.OwnerLabelKey]
|
||||
return a.DNSName == b.DNSName && a.Target == b.Target && a.RecordType == b.RecordType &&
|
||||
a.Labels[endpoint.OwnerLabelKey] == b.Labels[endpoint.OwnerLabelKey]
|
||||
}
|
||||
|
||||
// SameEndpoints compares two slices of endpoints regardless of order
|
||||
@ -37,34 +58,16 @@ func SameEndpoints(a, b []*endpoint.Endpoint) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
calculator := map[string]map[string]uint8{} //testutils is not meant for large data sets
|
||||
for _, recordA := range a {
|
||||
if _, exists := calculator[recordA.DNSName]; !exists {
|
||||
calculator[recordA.DNSName] = map[string]uint8{}
|
||||
}
|
||||
if _, exists := calculator[recordA.DNSName][recordA.Target]; !exists {
|
||||
calculator[recordA.DNSName][recordA.Target] = 0
|
||||
}
|
||||
calculator[recordA.DNSName][recordA.Target]++
|
||||
}
|
||||
for _, recordB := range b {
|
||||
if _, exists := calculator[recordB.DNSName]; !exists {
|
||||
sa := a[:]
|
||||
sb := b[:]
|
||||
sort.Sort(byAllFields(sa))
|
||||
sort.Sort(byAllFields(sb))
|
||||
|
||||
for i := range sa {
|
||||
if !SameEndpoint(sa[i], sb[i]) {
|
||||
return false
|
||||
}
|
||||
if _, exists := calculator[recordB.DNSName][recordB.Target]; !exists {
|
||||
return false
|
||||
}
|
||||
calculator[recordB.DNSName][recordB.Target]--
|
||||
}
|
||||
|
||||
for _, byDNSName := range calculator {
|
||||
for _, byCounter := range byDNSName {
|
||||
if byCounter != 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
63
internal/testutils/endpoint_test.go
Normal file
63
internal/testutils/endpoint_test.go
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package testutils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
)
|
||||
|
||||
func ExampleSameEndpoints() {
|
||||
eps := []*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "example.org",
|
||||
Target: "load-balancer.org",
|
||||
},
|
||||
{
|
||||
DNSName: "example.org",
|
||||
Target: "load-balancer.org",
|
||||
RecordType: "TXT",
|
||||
},
|
||||
{
|
||||
DNSName: "abc.com",
|
||||
Target: "something",
|
||||
RecordType: "TXT",
|
||||
},
|
||||
{
|
||||
DNSName: "abc.com",
|
||||
Target: "1.2.3.4",
|
||||
RecordType: "A",
|
||||
},
|
||||
{
|
||||
DNSName: "bbc.com",
|
||||
Target: "foo.com",
|
||||
RecordType: "CNAME",
|
||||
},
|
||||
}
|
||||
sort.Sort(byAllFields(eps))
|
||||
for _, ep := range eps {
|
||||
fmt.Println(ep)
|
||||
}
|
||||
// Output:
|
||||
// &{abc.com 1.2.3.4 A map[]}
|
||||
// &{abc.com something TXT map[]}
|
||||
// &{bbc.com foo.com CNAME map[]}
|
||||
// &{example.org load-balancer.org map[]}
|
||||
// &{example.org load-balancer.org TXT map[]}
|
||||
}
|
11
main.go
11
main.go
@ -94,7 +94,16 @@ func main() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
r, err := registry.NewNoopRegistry(p)
|
||||
var r registry.Registry
|
||||
switch cfg.Registry {
|
||||
case "noop":
|
||||
r, err = registry.NewNoopRegistry(p)
|
||||
case "txt":
|
||||
r, err = registry.NewTXTRegistry(p, cfg.TXTPrefix, cfg.RecordOwnerID)
|
||||
default:
|
||||
log.Fatalf("unknown registry: %s", cfg.Registry)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
@ -46,6 +46,9 @@ type Config struct {
|
||||
Debug bool
|
||||
LogFormat string
|
||||
Version bool
|
||||
Registry string
|
||||
RecordOwnerID string
|
||||
TXTPrefix string
|
||||
}
|
||||
|
||||
// NewConfig returns new Config object
|
||||
@ -65,11 +68,15 @@ func (cfg *Config) ParseFlags(args []string) error {
|
||||
flags.StringVar(&cfg.GoogleProject, "google-project", "", "gcloud project to target")
|
||||
flags.BoolVar(&cfg.Compatibility, "compatibility", false, "enable to process annotation semantics from legacy implementations")
|
||||
flags.StringVar(&cfg.MetricsAddress, "metrics-address", defaultMetricsAddress, "address to expose metrics on")
|
||||
flags.StringVar(&cfg.LogFormat, "log-format", defaultLogFormat, "log format output. options: [\"text\", \"json\"]")
|
||||
flags.StringVar(&cfg.LogFormat, "log-format", defaultLogFormat, "log format output: <text|json>")
|
||||
flags.DurationVar(&cfg.Interval, "interval", time.Minute, "interval between synchronizations")
|
||||
flags.BoolVar(&cfg.Once, "once", false, "run once and exit")
|
||||
flags.BoolVar(&cfg.DryRun, "dry-run", true, "dry-run mode")
|
||||
flags.BoolVar(&cfg.Debug, "debug", false, "debug mode")
|
||||
flags.BoolVar(&cfg.Version, "version", false, "display the version")
|
||||
flags.StringVar(&cfg.Registry, "registry", "noop", "type of registry for ownership: <noop|txt>")
|
||||
flags.StringVar(&cfg.RecordOwnerID, "record-owner-id", "", "id of the current external dns for labeling owned records")
|
||||
flags.StringVar(&cfg.TXTPrefix, "txt-prefix", "", `prefix of the associated TXT records DNS name; if --txt-prefix="abc-",
|
||||
corresponding txt record for CNAME [example.org] will have DNSName [abc-example.org]. Required for CNAME ownership support`)
|
||||
return flags.Parse(args)
|
||||
}
|
||||
|
@ -48,6 +48,9 @@ func TestParseFlags(t *testing.T) {
|
||||
Debug: false,
|
||||
LogFormat: defaultLogFormat,
|
||||
Version: false,
|
||||
Registry: "noop",
|
||||
RecordOwnerID: "",
|
||||
TXTPrefix: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -69,6 +72,9 @@ func TestParseFlags(t *testing.T) {
|
||||
Debug: false,
|
||||
LogFormat: defaultLogFormat,
|
||||
Version: false,
|
||||
Registry: "noop",
|
||||
RecordOwnerID: "",
|
||||
TXTPrefix: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -90,6 +96,9 @@ func TestParseFlags(t *testing.T) {
|
||||
Debug: false,
|
||||
LogFormat: defaultLogFormat,
|
||||
Version: false,
|
||||
Registry: "noop",
|
||||
RecordOwnerID: "",
|
||||
TXTPrefix: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -116,6 +125,9 @@ func TestParseFlags(t *testing.T) {
|
||||
Debug: false,
|
||||
LogFormat: "json",
|
||||
Version: false,
|
||||
Registry: "noop",
|
||||
RecordOwnerID: "",
|
||||
TXTPrefix: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -134,6 +146,9 @@ func TestParseFlags(t *testing.T) {
|
||||
"--once",
|
||||
"--dry-run=false",
|
||||
"--debug",
|
||||
"--registry=txt",
|
||||
"--record-owner-id=owner-1",
|
||||
"--txt-prefix=associated-txt-record",
|
||||
"--version"}},
|
||||
expected: &Config{
|
||||
InCluster: true,
|
||||
@ -151,6 +166,9 @@ func TestParseFlags(t *testing.T) {
|
||||
Debug: true,
|
||||
LogFormat: "yaml",
|
||||
Version: true,
|
||||
Registry: "txt",
|
||||
RecordOwnerID: "owner-1",
|
||||
TXTPrefix: "associated-txt-record",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -30,14 +30,14 @@ func TestCalculate(t *testing.T) {
|
||||
// empty list of records
|
||||
empty := []*endpoint.Endpoint{}
|
||||
// a simple entry
|
||||
fooV1 := []*endpoint.Endpoint{endpoint.NewEndpoint("foo", "v1")}
|
||||
fooV1 := []*endpoint.Endpoint{endpoint.NewEndpoint("foo", "v1", "CNAME")}
|
||||
// the same entry but with different target
|
||||
fooV2 := []*endpoint.Endpoint{endpoint.NewEndpoint("foo", "v2")}
|
||||
fooV2 := []*endpoint.Endpoint{endpoint.NewEndpoint("foo", "v2", "CNAME")}
|
||||
// another simple entry
|
||||
bar := []*endpoint.Endpoint{endpoint.NewEndpoint("bar", "v1")}
|
||||
bar := []*endpoint.Endpoint{endpoint.NewEndpoint("bar", "v1", "CNAME")}
|
||||
|
||||
// test case with labels
|
||||
noLabels := []*endpoint.Endpoint{endpoint.NewEndpoint("foo", "v2")}
|
||||
noLabels := []*endpoint.Endpoint{endpoint.NewEndpoint("foo", "v2", "CNAME")}
|
||||
labeledV2 := []*endpoint.Endpoint{newEndpointWithOwner("foo", "v2", "123")}
|
||||
labeledV1 := []*endpoint.Endpoint{newEndpointWithOwner("foo", "v1", "123")}
|
||||
|
||||
@ -77,10 +77,10 @@ func TestCalculate(t *testing.T) {
|
||||
|
||||
// BenchmarkCalculate benchmarks the Calculate method.
|
||||
func BenchmarkCalculate(b *testing.B) {
|
||||
foo := endpoint.NewEndpoint("foo", "v1")
|
||||
barV1 := endpoint.NewEndpoint("bar", "v1")
|
||||
barV2 := endpoint.NewEndpoint("bar", "v2")
|
||||
baz := endpoint.NewEndpoint("baz", "v1")
|
||||
foo := endpoint.NewEndpoint("foo", "v1", "")
|
||||
barV1 := endpoint.NewEndpoint("bar", "v1", "")
|
||||
barV2 := endpoint.NewEndpoint("bar", "v2", "")
|
||||
baz := endpoint.NewEndpoint("baz", "v1", "")
|
||||
|
||||
plan := &Plan{
|
||||
Current: []*endpoint.Endpoint{foo, barV1},
|
||||
@ -94,10 +94,10 @@ func BenchmarkCalculate(b *testing.B) {
|
||||
|
||||
// ExamplePlan shows how plan can be used.
|
||||
func ExamplePlan() {
|
||||
foo := endpoint.NewEndpoint("foo.example.com", "1.2.3.4")
|
||||
barV1 := endpoint.NewEndpoint("bar.example.com", "8.8.8.8")
|
||||
barV2 := endpoint.NewEndpoint("bar.example.com", "8.8.4.4")
|
||||
baz := endpoint.NewEndpoint("baz.example.com", "6.6.6.6")
|
||||
foo := endpoint.NewEndpoint("foo.example.com", "1.2.3.4", "")
|
||||
barV1 := endpoint.NewEndpoint("bar.example.com", "8.8.8.8", "")
|
||||
barV2 := endpoint.NewEndpoint("bar.example.com", "8.8.4.4", "")
|
||||
baz := endpoint.NewEndpoint("baz.example.com", "6.6.6.6", "")
|
||||
|
||||
// Plan where
|
||||
// * foo should be deleted
|
||||
@ -128,15 +128,14 @@ func ExamplePlan() {
|
||||
for _, ep := range plan.Changes.Delete {
|
||||
fmt.Println(ep)
|
||||
}
|
||||
// Output:
|
||||
// Create:
|
||||
// &{baz.example.com 6.6.6.6 map[]}
|
||||
// &{baz.example.com 6.6.6.6 map[] }
|
||||
// UpdateOld:
|
||||
// &{bar.example.com 8.8.8.8 map[]}
|
||||
// &{bar.example.com 8.8.8.8 map[] }
|
||||
// UpdateNew:
|
||||
// &{bar.example.com 8.8.4.4 map[]}
|
||||
// &{bar.example.com 8.8.4.4 map[] }
|
||||
// Delete:
|
||||
// &{foo.example.com 1.2.3.4 map[]}
|
||||
// &{foo.example.com 1.2.3.4 map[] }
|
||||
}
|
||||
|
||||
// validateEntries validates that the list of entries matches expected.
|
||||
@ -153,7 +152,7 @@ func validateEntries(t *testing.T, entries, expected []*endpoint.Endpoint) {
|
||||
}
|
||||
|
||||
func newEndpointWithOwner(dnsName, target, ownerID string) *endpoint.Endpoint {
|
||||
e := endpoint.NewEndpoint(dnsName, target)
|
||||
e := endpoint.NewEndpoint(dnsName, target, "CNAME")
|
||||
e.Labels[endpoint.OwnerLabelKey] = ownerID
|
||||
return e
|
||||
}
|
||||
|
@ -76,14 +76,14 @@ func (p *AWSProvider) Records(zone string) ([]*endpoint.Endpoint, error) {
|
||||
// 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:
|
||||
case route53.RRTypeA, route53.RRTypeCname, route53.RRTypeTxt:
|
||||
break
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
for _, rr := range r.ResourceRecords {
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint(aws.StringValue(r.Name), aws.StringValue(rr.Value)))
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint(aws.StringValue(r.Name), aws.StringValue(rr.Value), aws.StringValue(r.Type)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -181,7 +181,7 @@ func newChange(action string, endpoint *endpoint.Endpoint) *route53.Change {
|
||||
},
|
||||
},
|
||||
TTL: aws.Int64(300),
|
||||
Type: aws.String(suitableType(endpoint.Target)),
|
||||
Type: aws.String(suitableType(endpoint)),
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -133,16 +133,15 @@ func (r *Route53APIStub) CreateHostedZone(input *route53.CreateHostedZoneInput)
|
||||
|
||||
func TestAWSRecords(t *testing.T) {
|
||||
provider := newAWSProvider(t, false, []*endpoint.Endpoint{
|
||||
{DNSName: "list-test.ext-dns-test.teapot.zalan.do", Target: "8.8.8.8"},
|
||||
endpoint.NewEndpoint("list-test.ext-dns-test.teapot.zalan.do", "8.8.8.8", "A"),
|
||||
})
|
||||
|
||||
records, err := provider.Records(testZone)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
validateEndpoints(t, records, []*endpoint.Endpoint{
|
||||
{DNSName: "list-test.ext-dns-test.teapot.zalan.do", Target: "8.8.8.8"},
|
||||
endpoint.NewEndpoint("list-test.ext-dns-test.teapot.zalan.do", "8.8.8.8", "A"),
|
||||
})
|
||||
}
|
||||
|
||||
@ -150,7 +149,7 @@ func TestAWSCreateRecords(t *testing.T) {
|
||||
provider := newAWSProvider(t, false, []*endpoint.Endpoint{})
|
||||
|
||||
records := []*endpoint.Endpoint{
|
||||
{DNSName: "create-test.ext-dns-test.teapot.zalan.do", Target: "8.8.8.8"},
|
||||
endpoint.NewEndpoint("create-test.ext-dns-test.teapot.zalan.do", "8.8.8.8", ""),
|
||||
}
|
||||
|
||||
if err := provider.CreateRecords(testZone, records); err != nil {
|
||||
@ -163,20 +162,20 @@ func TestAWSCreateRecords(t *testing.T) {
|
||||
}
|
||||
|
||||
validateEndpoints(t, records, []*endpoint.Endpoint{
|
||||
{DNSName: "create-test.ext-dns-test.teapot.zalan.do", Target: "8.8.8.8"},
|
||||
endpoint.NewEndpoint("create-test.ext-dns-test.teapot.zalan.do", "8.8.8.8", "A"),
|
||||
})
|
||||
}
|
||||
|
||||
func TestAWSUpdateRecords(t *testing.T) {
|
||||
provider := newAWSProvider(t, false, []*endpoint.Endpoint{
|
||||
{DNSName: "update-test.ext-dns-test.teapot.zalan.do", Target: "8.8.8.8"},
|
||||
endpoint.NewEndpoint("update-test.ext-dns-test.teapot.zalan.do", "8.8.8.8", "A"),
|
||||
})
|
||||
|
||||
currentRecords := []*endpoint.Endpoint{
|
||||
{DNSName: "update-test.ext-dns-test.teapot.zalan.do", Target: "8.8.8.8"},
|
||||
endpoint.NewEndpoint("update-test.ext-dns-test.teapot.zalan.do", "8.8.8.8", "A"),
|
||||
}
|
||||
updatedRecords := []*endpoint.Endpoint{
|
||||
{DNSName: "update-test.ext-dns-test.teapot.zalan.do", Target: "1.2.3.4"},
|
||||
endpoint.NewEndpoint("update-test.ext-dns-test.teapot.zalan.do", "1.2.3.4", "A"),
|
||||
}
|
||||
|
||||
if err := provider.UpdateRecords(testZone, updatedRecords, currentRecords); err != nil {
|
||||
@ -189,17 +188,17 @@ func TestAWSUpdateRecords(t *testing.T) {
|
||||
}
|
||||
|
||||
validateEndpoints(t, records, []*endpoint.Endpoint{
|
||||
{DNSName: "update-test.ext-dns-test.teapot.zalan.do", Target: "1.2.3.4"},
|
||||
endpoint.NewEndpoint("update-test.ext-dns-test.teapot.zalan.do", "1.2.3.4", "A"),
|
||||
})
|
||||
}
|
||||
|
||||
func TestAWSDeleteRecords(t *testing.T) {
|
||||
provider := newAWSProvider(t, false, []*endpoint.Endpoint{
|
||||
{DNSName: "delete-test.ext-dns-test.teapot.zalan.do", Target: "8.8.8.8"},
|
||||
endpoint.NewEndpoint("delete-test.ext-dns-test.teapot.zalan.do", "8.8.8.8", "A"),
|
||||
})
|
||||
|
||||
currentRecords := []*endpoint.Endpoint{
|
||||
{DNSName: "delete-test.ext-dns-test.teapot.zalan.do", Target: "8.8.8.8"},
|
||||
endpoint.NewEndpoint("delete-test.ext-dns-test.teapot.zalan.do", "8.8.8.8", ""),
|
||||
}
|
||||
|
||||
if err := provider.DeleteRecords(testZone, currentRecords); err != nil {
|
||||
@ -216,23 +215,23 @@ func TestAWSDeleteRecords(t *testing.T) {
|
||||
|
||||
func TestAWSApplyChanges(t *testing.T) {
|
||||
provider := newAWSProvider(t, false, []*endpoint.Endpoint{
|
||||
{DNSName: "update-test.ext-dns-test.teapot.zalan.do", Target: "8.8.8.8"},
|
||||
{DNSName: "delete-test.ext-dns-test.teapot.zalan.do", Target: "8.8.8.8"},
|
||||
endpoint.NewEndpoint("update-test.ext-dns-test.teapot.zalan.do", "8.8.8.8", "A"),
|
||||
endpoint.NewEndpoint("delete-test.ext-dns-test.teapot.zalan.do", "8.8.8.8", "A"),
|
||||
})
|
||||
|
||||
createRecords := []*endpoint.Endpoint{
|
||||
{DNSName: "create-test.ext-dns-test.teapot.zalan.do", Target: "8.8.8.8"},
|
||||
endpoint.NewEndpoint("create-test.ext-dns-test.teapot.zalan.do", "8.8.8.8", ""),
|
||||
}
|
||||
|
||||
currentRecords := []*endpoint.Endpoint{
|
||||
{DNSName: "update-test.ext-dns-test.teapot.zalan.do", Target: "8.8.8.8"},
|
||||
endpoint.NewEndpoint("update-test.ext-dns-test.teapot.zalan.do", "8.8.8.8", ""),
|
||||
}
|
||||
updatedRecords := []*endpoint.Endpoint{
|
||||
{DNSName: "update-test.ext-dns-test.teapot.zalan.do", Target: "1.2.3.4"},
|
||||
endpoint.NewEndpoint("update-test.ext-dns-test.teapot.zalan.do", "1.2.3.4", ""),
|
||||
}
|
||||
|
||||
deleteRecords := []*endpoint.Endpoint{
|
||||
{DNSName: "delete-test.ext-dns-test.teapot.zalan.do", Target: "8.8.8.8"},
|
||||
endpoint.NewEndpoint("delete-test.ext-dns-test.teapot.zalan.do", "8.8.8.8", ""),
|
||||
}
|
||||
|
||||
changes := &plan.Changes{
|
||||
@ -252,8 +251,8 @@ func TestAWSApplyChanges(t *testing.T) {
|
||||
}
|
||||
|
||||
validateEndpoints(t, records, []*endpoint.Endpoint{
|
||||
{DNSName: "create-test.ext-dns-test.teapot.zalan.do", Target: "8.8.8.8"},
|
||||
{DNSName: "update-test.ext-dns-test.teapot.zalan.do", Target: "1.2.3.4"},
|
||||
endpoint.NewEndpoint("create-test.ext-dns-test.teapot.zalan.do", "8.8.8.8", "A"),
|
||||
endpoint.NewEndpoint("update-test.ext-dns-test.teapot.zalan.do", "1.2.3.4", "A"),
|
||||
})
|
||||
}
|
||||
|
||||
@ -269,7 +268,7 @@ func TestAWSCreateRecordsDryRun(t *testing.T) {
|
||||
provider := newAWSProvider(t, true, []*endpoint.Endpoint{})
|
||||
|
||||
records := []*endpoint.Endpoint{
|
||||
{DNSName: "create-test.ext-dns-test.teapot.zalan.do", Target: "8.8.8.8"},
|
||||
endpoint.NewEndpoint("create-test.ext-dns-test.teapot.zalan.do", "8.8.8.8", ""),
|
||||
}
|
||||
|
||||
if err := provider.CreateRecords(testZone, records); err != nil {
|
||||
@ -286,14 +285,14 @@ func TestAWSCreateRecordsDryRun(t *testing.T) {
|
||||
|
||||
func TestAWSUpdateRecordsDryRun(t *testing.T) {
|
||||
provider := newAWSProvider(t, true, []*endpoint.Endpoint{
|
||||
{DNSName: "update-test.ext-dns-test.teapot.zalan.do", Target: "8.8.8.8"},
|
||||
endpoint.NewEndpoint("update-test.ext-dns-test.teapot.zalan.do", "8.8.8.8", "A"),
|
||||
})
|
||||
|
||||
currentRecords := []*endpoint.Endpoint{
|
||||
{DNSName: "update-test.ext-dns-test.teapot.zalan.do", Target: "8.8.8.8"},
|
||||
endpoint.NewEndpoint("update-test.ext-dns-test.teapot.zalan.do", "8.8.8.8", ""),
|
||||
}
|
||||
updatedRecords := []*endpoint.Endpoint{
|
||||
{DNSName: "update-test.ext-dns-test.teapot.zalan.do", Target: "1.2.3.4"},
|
||||
endpoint.NewEndpoint("update-test.ext-dns-test.teapot.zalan.do", "1.2.3.4", ""),
|
||||
}
|
||||
|
||||
if err := provider.UpdateRecords(testZone, updatedRecords, currentRecords); err != nil {
|
||||
@ -306,17 +305,17 @@ func TestAWSUpdateRecordsDryRun(t *testing.T) {
|
||||
}
|
||||
|
||||
validateEndpoints(t, records, []*endpoint.Endpoint{
|
||||
{DNSName: "update-test.ext-dns-test.teapot.zalan.do", Target: "8.8.8.8"},
|
||||
endpoint.NewEndpoint("update-test.ext-dns-test.teapot.zalan.do", "8.8.8.8", "A"),
|
||||
})
|
||||
}
|
||||
|
||||
func TestAWSDeleteRecordsDryRun(t *testing.T) {
|
||||
provider := newAWSProvider(t, true, []*endpoint.Endpoint{
|
||||
{DNSName: "delete-test.ext-dns-test.teapot.zalan.do", Target: "8.8.8.8"},
|
||||
endpoint.NewEndpoint("delete-test.ext-dns-test.teapot.zalan.do", "8.8.8.8", "A"),
|
||||
})
|
||||
|
||||
currentRecords := []*endpoint.Endpoint{
|
||||
{DNSName: "delete-test.ext-dns-test.teapot.zalan.do", Target: "8.8.8.8"},
|
||||
endpoint.NewEndpoint("delete-test.ext-dns-test.teapot.zalan.do", "8.8.8.8", ""),
|
||||
}
|
||||
|
||||
if err := provider.DeleteRecords(testZone, currentRecords); err != nil {
|
||||
@ -329,29 +328,29 @@ func TestAWSDeleteRecordsDryRun(t *testing.T) {
|
||||
}
|
||||
|
||||
validateEndpoints(t, records, []*endpoint.Endpoint{
|
||||
{DNSName: "delete-test.ext-dns-test.teapot.zalan.do", Target: "8.8.8.8"},
|
||||
endpoint.NewEndpoint("delete-test.ext-dns-test.teapot.zalan.do", "8.8.8.8", "A"),
|
||||
})
|
||||
}
|
||||
|
||||
func TestAWSApplyChangesDryRun(t *testing.T) {
|
||||
provider := newAWSProvider(t, true, []*endpoint.Endpoint{
|
||||
{DNSName: "update-test.ext-dns-test.teapot.zalan.do", Target: "8.8.8.8"},
|
||||
{DNSName: "delete-test.ext-dns-test.teapot.zalan.do", Target: "8.8.8.8"},
|
||||
endpoint.NewEndpoint("update-test.ext-dns-test.teapot.zalan.do", "8.8.8.8", "A"),
|
||||
endpoint.NewEndpoint("delete-test.ext-dns-test.teapot.zalan.do", "8.8.8.8", "A"),
|
||||
})
|
||||
|
||||
createRecords := []*endpoint.Endpoint{
|
||||
{DNSName: "create-test.ext-dns-test.teapot.zalan.do", Target: "8.8.8.8"},
|
||||
endpoint.NewEndpoint("create-test.ext-dns-test.teapot.zalan.do", "8.8.8.8", ""),
|
||||
}
|
||||
|
||||
currentRecords := []*endpoint.Endpoint{
|
||||
{DNSName: "update-test.ext-dns-test.teapot.zalan.do", Target: "8.8.8.8"},
|
||||
endpoint.NewEndpoint("update-test.ext-dns-test.teapot.zalan.do", "8.8.8.8", ""),
|
||||
}
|
||||
updatedRecords := []*endpoint.Endpoint{
|
||||
{DNSName: "update-test.ext-dns-test.teapot.zalan.do", Target: "1.2.3.4"},
|
||||
endpoint.NewEndpoint("update-test.ext-dns-test.teapot.zalan.do", "1.2.3.4", ""),
|
||||
}
|
||||
|
||||
deleteRecords := []*endpoint.Endpoint{
|
||||
{DNSName: "delete-test.ext-dns-test.teapot.zalan.do", Target: "8.8.8.8"},
|
||||
endpoint.NewEndpoint("delete-test.ext-dns-test.teapot.zalan.do", "8.8.8.8", ""),
|
||||
}
|
||||
|
||||
changes := &plan.Changes{
|
||||
@ -371,8 +370,8 @@ func TestAWSApplyChangesDryRun(t *testing.T) {
|
||||
}
|
||||
|
||||
validateEndpoints(t, records, []*endpoint.Endpoint{
|
||||
{DNSName: "update-test.ext-dns-test.teapot.zalan.do", Target: "8.8.8.8"},
|
||||
{DNSName: "delete-test.ext-dns-test.teapot.zalan.do", Target: "8.8.8.8"},
|
||||
endpoint.NewEndpoint("update-test.ext-dns-test.teapot.zalan.do", "8.8.8.8", "A"),
|
||||
endpoint.NewEndpoint("delete-test.ext-dns-test.teapot.zalan.do", "8.8.8.8", "A"),
|
||||
})
|
||||
}
|
||||
|
||||
@ -380,7 +379,7 @@ func TestAWSCreateRecordsCNAME(t *testing.T) {
|
||||
provider := newAWSProvider(t, false, []*endpoint.Endpoint{})
|
||||
|
||||
records := []*endpoint.Endpoint{
|
||||
{DNSName: "create-test.ext-dns-test.teapot.zalan.do", Target: "foo.elb.amazonaws.com"},
|
||||
endpoint.NewEndpoint("create-test.ext-dns-test.teapot.zalan.do", "foo.elb.amazonaws.com", ""),
|
||||
}
|
||||
|
||||
if err := provider.CreateRecords(testZone, records); err != nil {
|
||||
@ -393,20 +392,20 @@ func TestAWSCreateRecordsCNAME(t *testing.T) {
|
||||
}
|
||||
|
||||
validateEndpoints(t, records, []*endpoint.Endpoint{
|
||||
{DNSName: "create-test.ext-dns-test.teapot.zalan.do", Target: "foo.elb.amazonaws.com"},
|
||||
endpoint.NewEndpoint("create-test.ext-dns-test.teapot.zalan.do", "foo.elb.amazonaws.com", "CNAME"),
|
||||
})
|
||||
}
|
||||
|
||||
func TestAWSUpdateRecordsCNAME(t *testing.T) {
|
||||
provider := newAWSProvider(t, false, []*endpoint.Endpoint{
|
||||
{DNSName: "update-test.ext-dns-test.teapot.zalan.do", Target: "foo.elb.amazonaws.com"},
|
||||
endpoint.NewEndpoint("update-test.ext-dns-test.teapot.zalan.do", "foo.elb.amazonaws.com", "CNAME"),
|
||||
})
|
||||
|
||||
currentRecords := []*endpoint.Endpoint{
|
||||
{DNSName: "update-test.ext-dns-test.teapot.zalan.do", Target: "foo.elb.amazonaws.com"},
|
||||
endpoint.NewEndpoint("update-test.ext-dns-test.teapot.zalan.do", "foo.elb.amazonaws.com", ""),
|
||||
}
|
||||
updatedRecords := []*endpoint.Endpoint{
|
||||
{DNSName: "update-test.ext-dns-test.teapot.zalan.do", Target: "bar.elb.amazonaws.com"},
|
||||
endpoint.NewEndpoint("update-test.ext-dns-test.teapot.zalan.do", "bar.elb.amazonaws.com", ""),
|
||||
}
|
||||
|
||||
if err := provider.UpdateRecords(testZone, updatedRecords, currentRecords); err != nil {
|
||||
@ -419,17 +418,17 @@ func TestAWSUpdateRecordsCNAME(t *testing.T) {
|
||||
}
|
||||
|
||||
validateEndpoints(t, records, []*endpoint.Endpoint{
|
||||
{DNSName: "update-test.ext-dns-test.teapot.zalan.do", Target: "bar.elb.amazonaws.com"},
|
||||
endpoint.NewEndpoint("update-test.ext-dns-test.teapot.zalan.do", "bar.elb.amazonaws.com", "CNAME"),
|
||||
})
|
||||
}
|
||||
|
||||
func TestAWSDeleteRecordsCNAME(t *testing.T) {
|
||||
provider := newAWSProvider(t, false, []*endpoint.Endpoint{
|
||||
{DNSName: "delete-test.ext-dns-test.teapot.zalan.do", Target: "baz.elb.amazonaws.com"},
|
||||
endpoint.NewEndpoint("delete-test.ext-dns-test.teapot.zalan.do", "baz.elb.amazonaws.com", "CNAME"),
|
||||
})
|
||||
|
||||
currentRecords := []*endpoint.Endpoint{
|
||||
{DNSName: "delete-test.ext-dns-test.teapot.zalan.do", Target: "baz.elb.amazonaws.com"},
|
||||
endpoint.NewEndpoint("delete-test.ext-dns-test.teapot.zalan.do", "baz.elb.amazonaws.com", ""),
|
||||
}
|
||||
|
||||
if err := provider.DeleteRecords(testZone, currentRecords); err != nil {
|
||||
@ -446,23 +445,23 @@ func TestAWSDeleteRecordsCNAME(t *testing.T) {
|
||||
|
||||
func TestAWSApplyChangesCNAME(t *testing.T) {
|
||||
provider := newAWSProvider(t, false, []*endpoint.Endpoint{
|
||||
{DNSName: "update-test.ext-dns-test.teapot.zalan.do", Target: "foo.elb.amazonaws.com"},
|
||||
{DNSName: "delete-test.ext-dns-test.teapot.zalan.do", Target: "qux.elb.amazonaws.com"},
|
||||
endpoint.NewEndpoint("update-test.ext-dns-test.teapot.zalan.do", "foo.elb.amazonaws.com", "CNAME"),
|
||||
endpoint.NewEndpoint("delete-test.ext-dns-test.teapot.zalan.do", "qux.elb.amazonaws.com", "CNAME"),
|
||||
})
|
||||
|
||||
createRecords := []*endpoint.Endpoint{
|
||||
{DNSName: "create-test.ext-dns-test.teapot.zalan.do", Target: "foo.elb.amazonaws.com"},
|
||||
endpoint.NewEndpoint("create-test.ext-dns-test.teapot.zalan.do", "foo.elb.amazonaws.com", ""),
|
||||
}
|
||||
|
||||
currentRecords := []*endpoint.Endpoint{
|
||||
{DNSName: "update-test.ext-dns-test.teapot.zalan.do", Target: "bar.elb.amazonaws.com"},
|
||||
endpoint.NewEndpoint("update-test.ext-dns-test.teapot.zalan.do", "bar.elb.amazonaws.com", ""),
|
||||
}
|
||||
updatedRecords := []*endpoint.Endpoint{
|
||||
{DNSName: "update-test.ext-dns-test.teapot.zalan.do", Target: "baz.elb.amazonaws.com"},
|
||||
endpoint.NewEndpoint("update-test.ext-dns-test.teapot.zalan.do", "baz.elb.amazonaws.com", ""),
|
||||
}
|
||||
|
||||
deleteRecords := []*endpoint.Endpoint{
|
||||
{DNSName: "delete-test.ext-dns-test.teapot.zalan.do", Target: "qux.elb.amazonaws.com"},
|
||||
endpoint.NewEndpoint("delete-test.ext-dns-test.teapot.zalan.do", "qux.elb.amazonaws.com", ""),
|
||||
}
|
||||
|
||||
changes := &plan.Changes{
|
||||
@ -482,14 +481,14 @@ func TestAWSApplyChangesCNAME(t *testing.T) {
|
||||
}
|
||||
|
||||
validateEndpoints(t, records, []*endpoint.Endpoint{
|
||||
{DNSName: "create-test.ext-dns-test.teapot.zalan.do", Target: "foo.elb.amazonaws.com"},
|
||||
{DNSName: "update-test.ext-dns-test.teapot.zalan.do", Target: "baz.elb.amazonaws.com"},
|
||||
endpoint.NewEndpoint("create-test.ext-dns-test.teapot.zalan.do", "foo.elb.amazonaws.com", "CNAME"),
|
||||
endpoint.NewEndpoint("update-test.ext-dns-test.teapot.zalan.do", "baz.elb.amazonaws.com", "CNAME"),
|
||||
})
|
||||
}
|
||||
|
||||
func TestAWSSanitizeZone(t *testing.T) {
|
||||
provider := newAWSProvider(t, false, []*endpoint.Endpoint{
|
||||
{DNSName: "list-test.ext-dns-test.teapot.zalan.do", Target: "8.8.8.8"},
|
||||
endpoint.NewEndpoint("list-test.ext-dns-test.teapot.zalan.do", "8.8.8.8", "A"),
|
||||
})
|
||||
|
||||
records, err := provider.Records(testZone)
|
||||
@ -498,7 +497,7 @@ func TestAWSSanitizeZone(t *testing.T) {
|
||||
}
|
||||
|
||||
validateEndpoints(t, records, []*endpoint.Endpoint{
|
||||
{DNSName: "list-test.ext-dns-test.teapot.zalan.do", Target: "8.8.8.8"},
|
||||
endpoint.NewEndpoint("list-test.ext-dns-test.teapot.zalan.do", "8.8.8.8", "A"),
|
||||
})
|
||||
|
||||
records, err = provider.Records("/hostedzone/" + testZone)
|
||||
@ -507,13 +506,13 @@ func TestAWSSanitizeZone(t *testing.T) {
|
||||
}
|
||||
|
||||
validateEndpoints(t, records, []*endpoint.Endpoint{
|
||||
{DNSName: "list-test.ext-dns-test.teapot.zalan.do", Target: "8.8.8.8"},
|
||||
endpoint.NewEndpoint("list-test.ext-dns-test.teapot.zalan.do", "8.8.8.8", "A"),
|
||||
})
|
||||
}
|
||||
|
||||
func validateEndpoints(t *testing.T, endpoints []*endpoint.Endpoint, expected []*endpoint.Endpoint) {
|
||||
if !testutils.SameEndpoints(endpoints, expected) {
|
||||
t.Errorf("expected %v, got %v", expected, endpoints)
|
||||
t.Fatalf("expected %v, got %v", expected, endpoints)
|
||||
}
|
||||
}
|
||||
|
||||
@ -552,7 +551,6 @@ func setupRecords(t *testing.T, provider *AWSProvider, endpoints []*endpoint.End
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
validateEndpoints(t, records, endpoints)
|
||||
}
|
||||
|
||||
|
@ -185,7 +185,7 @@ func (p *googleProvider) Records(zone string) (endpoints []*endpoint.Endpoint, _
|
||||
// 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":
|
||||
case "A", "CNAME", "TXT":
|
||||
break
|
||||
default:
|
||||
continue
|
||||
@ -193,7 +193,7 @@ func (p *googleProvider) Records(zone string) (endpoints []*endpoint.Endpoint, _
|
||||
|
||||
for _, rr := range r.Rrdatas {
|
||||
// each page is processed sequentially, no need for a mutex here.
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint(r.Name, rr))
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint(r.Name, rr, r.Type))
|
||||
}
|
||||
}
|
||||
|
||||
@ -294,7 +294,7 @@ func newRecord(endpoint *endpoint.Endpoint) *dns.ResourceRecordSet {
|
||||
Name: endpoint.DNSName,
|
||||
Rrdatas: []string{endpoint.Target},
|
||||
Ttl: 300,
|
||||
Type: suitableType(endpoint.Target),
|
||||
Type: suitableType(endpoint),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,8 +24,6 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
defaultType = "A"
|
||||
|
||||
// ErrZoneAlreadyExists error returned when zone cannot be created when it already exists
|
||||
ErrZoneAlreadyExists = errors.New("specified zone already exists")
|
||||
// ErrZoneNotFound error returned when specified zone does not exists
|
||||
@ -35,7 +33,7 @@ var (
|
||||
// ErrRecordNotFound when update/delete request is sent but record not found
|
||||
ErrRecordNotFound = errors.New("record not found")
|
||||
// ErrInvalidBatchRequest when record is repeated in create/update/delete
|
||||
ErrInvalidBatchRequest = errors.New("record should only be specified in one list")
|
||||
ErrInvalidBatchRequest = errors.New("invalid batch request")
|
||||
)
|
||||
|
||||
type zone map[string][]*InMemoryRecord
|
||||
@ -100,18 +98,25 @@ func (im *InMemoryProvider) ApplyChanges(zone string, changes *plan.Changes) err
|
||||
|
||||
for _, newEndpoint := range changes.Create {
|
||||
im.zones[zone][newEndpoint.DNSName] = append(im.zones[zone][newEndpoint.DNSName], &InMemoryRecord{
|
||||
Type: defaultType,
|
||||
Type: suitableType(newEndpoint),
|
||||
Endpoint: newEndpoint,
|
||||
})
|
||||
}
|
||||
for _, updateEndpoint := range changes.UpdateNew {
|
||||
recordToUpdate := im.findByType(defaultType, im.zones[zone][updateEndpoint.DNSName])
|
||||
recordToUpdate.Target = updateEndpoint.Target
|
||||
for _, curEndpoint := range changes.UpdateOld {
|
||||
if curEndpoint.DNSName == updateEndpoint.DNSName && curEndpoint.RecordType == updateEndpoint.RecordType {
|
||||
for _, recordToUpdate := range im.zones[zone][updateEndpoint.DNSName] {
|
||||
if recordToUpdate.Target == curEndpoint.Target {
|
||||
recordToUpdate.Target = updateEndpoint.Target
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, deleteEndpoint := range changes.Delete {
|
||||
newRecordSet := make([]*InMemoryRecord, 0)
|
||||
for _, record := range im.zones[zone][deleteEndpoint.DNSName] {
|
||||
if record.Type != defaultType {
|
||||
if record.Type != suitableType(deleteEndpoint) {
|
||||
newRecordSet = append(newRecordSet, record)
|
||||
}
|
||||
}
|
||||
@ -126,38 +131,50 @@ func (im *InMemoryProvider) validateChangeBatch(zone string, changes *plan.Chang
|
||||
if !ok {
|
||||
return ErrZoneNotFound
|
||||
}
|
||||
mesh := map[string]bool{}
|
||||
mesh := map[string]map[string]bool{}
|
||||
for _, newEndpoint := range changes.Create {
|
||||
if im.findByType(defaultType, existing[newEndpoint.DNSName]) != nil {
|
||||
if im.findByType(suitableType(newEndpoint), existing[newEndpoint.DNSName]) != nil {
|
||||
return ErrRecordAlreadyExists
|
||||
}
|
||||
if _, exists := mesh[newEndpoint.DNSName]; exists {
|
||||
return ErrInvalidBatchRequest
|
||||
if mesh[newEndpoint.DNSName][suitableType(newEndpoint)] {
|
||||
return ErrInvalidBatchRequest
|
||||
}
|
||||
mesh[newEndpoint.DNSName][suitableType(newEndpoint)] = true
|
||||
continue
|
||||
}
|
||||
mesh[newEndpoint.DNSName] = true
|
||||
mesh[newEndpoint.DNSName] = map[string]bool{suitableType(newEndpoint): true}
|
||||
}
|
||||
for _, updateEndpoint := range changes.UpdateNew {
|
||||
if im.findByType(defaultType, existing[updateEndpoint.DNSName]) == nil {
|
||||
if im.findByType(suitableType(updateEndpoint), existing[updateEndpoint.DNSName]) == nil {
|
||||
return ErrRecordNotFound
|
||||
}
|
||||
if _, exists := mesh[updateEndpoint.DNSName]; exists {
|
||||
return ErrInvalidBatchRequest
|
||||
if mesh[updateEndpoint.DNSName][suitableType(updateEndpoint)] {
|
||||
return ErrInvalidBatchRequest
|
||||
}
|
||||
mesh[updateEndpoint.DNSName][suitableType(updateEndpoint)] = true
|
||||
continue
|
||||
}
|
||||
mesh[updateEndpoint.DNSName] = true
|
||||
mesh[updateEndpoint.DNSName] = map[string]bool{suitableType(updateEndpoint): true}
|
||||
}
|
||||
for _, updateOldEndpoint := range changes.UpdateOld {
|
||||
if rec := im.findByType(defaultType, existing[updateOldEndpoint.DNSName]); rec == nil || rec.Target != updateOldEndpoint.Target {
|
||||
if rec := im.findByType(suitableType(updateOldEndpoint), existing[updateOldEndpoint.DNSName]); rec == nil || rec.Target != updateOldEndpoint.Target {
|
||||
return ErrRecordNotFound
|
||||
}
|
||||
}
|
||||
for _, deleteEndpoint := range changes.Delete {
|
||||
if rec := im.findByType(defaultType, existing[deleteEndpoint.DNSName]); rec == nil || rec.Target != deleteEndpoint.Target {
|
||||
if rec := im.findByType(suitableType(deleteEndpoint), existing[deleteEndpoint.DNSName]); rec == nil || rec.Target != deleteEndpoint.Target {
|
||||
return ErrRecordNotFound
|
||||
}
|
||||
if _, exists := mesh[deleteEndpoint.DNSName]; exists {
|
||||
return ErrInvalidBatchRequest
|
||||
if mesh[deleteEndpoint.DNSName][suitableType(deleteEndpoint)] {
|
||||
return ErrInvalidBatchRequest
|
||||
}
|
||||
mesh[deleteEndpoint.DNSName][suitableType(deleteEndpoint)] = true
|
||||
continue
|
||||
}
|
||||
mesh[deleteEndpoint.DNSName] = true
|
||||
mesh[deleteEndpoint.DNSName] = map[string]bool{suitableType(deleteEndpoint): true}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -176,6 +193,7 @@ func (im *InMemoryProvider) endpoints(zone string) []*endpoint.Endpoint {
|
||||
if zoneRecords, exists := im.zones[zone]; exists {
|
||||
for _, recordsPerName := range zoneRecords {
|
||||
for _, record := range recordsPerName {
|
||||
record.Endpoint.RecordType = record.Type
|
||||
endpoints = append(endpoints, record.Endpoint)
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,9 @@ import (
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
)
|
||||
|
||||
var _ Provider = &InMemoryProvider{}
|
||||
var (
|
||||
_ Provider = &InMemoryProvider{}
|
||||
)
|
||||
|
||||
func TestInMemoryProvider(t *testing.T) {
|
||||
t.Run("Records", testInMemoryRecords)
|
||||
@ -201,7 +203,7 @@ func testInMemoryEndpoints(t *testing.T) {
|
||||
DNSName: "example.org",
|
||||
Target: "8.8.8.8",
|
||||
},
|
||||
Type: defaultType,
|
||||
Type: "A",
|
||||
},
|
||||
{
|
||||
Endpoint: &endpoint.Endpoint{
|
||||
@ -214,7 +216,7 @@ func testInMemoryEndpoints(t *testing.T) {
|
||||
{
|
||||
Endpoint: &endpoint.Endpoint{
|
||||
DNSName: "foo.org",
|
||||
Target: "4.4.4.4",
|
||||
Target: "bar.org",
|
||||
},
|
||||
Type: "CNAME",
|
||||
},
|
||||
@ -227,22 +229,25 @@ func testInMemoryEndpoints(t *testing.T) {
|
||||
DNSName: "example.com",
|
||||
Target: "4.4.4.4",
|
||||
},
|
||||
Type: "CNAME",
|
||||
Type: "A",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: []*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "example.org",
|
||||
Target: "8.8.8.8",
|
||||
DNSName: "example.org",
|
||||
Target: "8.8.8.8",
|
||||
RecordType: "A",
|
||||
},
|
||||
{
|
||||
DNSName: "example.org",
|
||||
DNSName: "example.org",
|
||||
RecordType: "TXT",
|
||||
},
|
||||
{
|
||||
DNSName: "foo.org",
|
||||
Target: "4.4.4.4",
|
||||
DNSName: "foo.org",
|
||||
Target: "bar.org",
|
||||
RecordType: "CNAME",
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -289,7 +294,7 @@ func testInMemoryRecords(t *testing.T) {
|
||||
DNSName: "example.org",
|
||||
Target: "8.8.8.8",
|
||||
},
|
||||
Type: defaultType,
|
||||
Type: "A",
|
||||
},
|
||||
{
|
||||
Endpoint: &endpoint.Endpoint{
|
||||
@ -352,7 +357,7 @@ func testInMemoryValidateChangeBatch(t *testing.T) {
|
||||
DNSName: "example.org",
|
||||
Target: "8.8.8.8",
|
||||
},
|
||||
Type: defaultType,
|
||||
Type: "A",
|
||||
},
|
||||
{
|
||||
Endpoint: &endpoint.Endpoint{
|
||||
@ -365,7 +370,7 @@ func testInMemoryValidateChangeBatch(t *testing.T) {
|
||||
{
|
||||
Endpoint: &endpoint.Endpoint{
|
||||
DNSName: "foo.org",
|
||||
Target: "4.4.4.4",
|
||||
Target: "bar.org",
|
||||
},
|
||||
Type: "CNAME",
|
||||
},
|
||||
@ -376,7 +381,7 @@ func testInMemoryValidateChangeBatch(t *testing.T) {
|
||||
DNSName: "foo.bar.org",
|
||||
Target: "5.5.5.5",
|
||||
},
|
||||
Type: defaultType,
|
||||
Type: "A",
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -385,7 +390,7 @@ func testInMemoryValidateChangeBatch(t *testing.T) {
|
||||
{
|
||||
Endpoint: &endpoint.Endpoint{
|
||||
DNSName: "example.com",
|
||||
Target: "4.4.4.4",
|
||||
Target: "another-example.com",
|
||||
},
|
||||
Type: "CNAME",
|
||||
},
|
||||
@ -719,7 +724,7 @@ func testInMemoryApplyChanges(t *testing.T) {
|
||||
DNSName: "example.org",
|
||||
Target: "8.8.8.8",
|
||||
},
|
||||
Type: defaultType,
|
||||
Type: "A",
|
||||
},
|
||||
{
|
||||
Endpoint: &endpoint.Endpoint{
|
||||
@ -807,7 +812,7 @@ func testInMemoryApplyChanges(t *testing.T) {
|
||||
DNSName: "foo.bar.org",
|
||||
Target: "4.8.8.4",
|
||||
},
|
||||
Type: defaultType,
|
||||
Type: "A",
|
||||
},
|
||||
},
|
||||
"foo.bar.new.org": []*InMemoryRecord{
|
||||
@ -816,7 +821,7 @@ func testInMemoryApplyChanges(t *testing.T) {
|
||||
DNSName: "foo.bar.new.org",
|
||||
Target: "4.8.8.9",
|
||||
},
|
||||
Type: defaultType,
|
||||
Type: "A",
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -843,7 +848,7 @@ func testInMemoryApplyChanges(t *testing.T) {
|
||||
DNSName: "example.org",
|
||||
Target: "8.8.8.8",
|
||||
},
|
||||
Type: defaultType,
|
||||
Type: "A",
|
||||
},
|
||||
{
|
||||
Endpoint: &endpoint.Endpoint{
|
||||
@ -867,7 +872,7 @@ func testInMemoryApplyChanges(t *testing.T) {
|
||||
DNSName: "foo.bar.org",
|
||||
Target: "5.5.5.5",
|
||||
},
|
||||
Type: defaultType,
|
||||
Type: "A",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -31,10 +31,12 @@ type Provider interface {
|
||||
|
||||
// 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 {
|
||||
func suitableType(ep *endpoint.Endpoint) string {
|
||||
if ep.RecordType != "" {
|
||||
return ep.RecordType
|
||||
}
|
||||
if net.ParseIP(ep.Target) == nil {
|
||||
return "CNAME"
|
||||
}
|
||||
|
||||
return "A"
|
||||
}
|
||||
|
@ -80,17 +80,19 @@ func testNoopApplyChanges(t *testing.T) {
|
||||
providerRecords := []*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "example.org",
|
||||
Target: "8.8.8.8",
|
||||
Target: "old-lb.com",
|
||||
},
|
||||
}
|
||||
expectedUpdate := []*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "example.org",
|
||||
Target: "new-example-lb.com",
|
||||
DNSName: "example.org",
|
||||
Target: "new-example-lb.com",
|
||||
RecordType: "CNAME",
|
||||
},
|
||||
{
|
||||
DNSName: "new-record.org",
|
||||
Target: "new-lb.org",
|
||||
DNSName: "new-record.org",
|
||||
Target: "new-lb.org",
|
||||
RecordType: "CNAME",
|
||||
},
|
||||
}
|
||||
|
||||
@ -136,7 +138,7 @@ func testNoopApplyChanges(t *testing.T) {
|
||||
UpdateOld: []*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "example.org",
|
||||
Target: "8.8.8.8",
|
||||
Target: "old-lb.com",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -29,3 +29,14 @@ type Registry interface {
|
||||
Records(zone string) ([]*endpoint.Endpoint, error)
|
||||
ApplyChanges(zone string, changes *plan.Changes) error
|
||||
}
|
||||
|
||||
//TODO(ideahitme): consider moving this to Plan
|
||||
func filterOwnedRecords(ownerID string, eps []*endpoint.Endpoint) []*endpoint.Endpoint {
|
||||
filtered := []*endpoint.Endpoint{}
|
||||
for _, ep := range eps {
|
||||
if ep.Labels[endpoint.OwnerLabelKey] == ownerID {
|
||||
filtered = append(filtered, ep)
|
||||
}
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
160
registry/txt.go
Normal file
160
registry/txt.go
Normal file
@ -0,0 +1,160 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package registry
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
"github.com/kubernetes-incubator/external-dns/provider"
|
||||
)
|
||||
|
||||
var (
|
||||
txtLabelRegex = regexp.MustCompile("^\"heritage=external-dns;external-dns/record-owner-id=(.+)\"")
|
||||
txtLabelFormat = "\"heritage=external-dns;external-dns/record-owner-id=%s\""
|
||||
)
|
||||
|
||||
// TXTRegistry implements registry interface with ownership implemented via associated TXT records
|
||||
type TXTRegistry struct {
|
||||
provider provider.Provider
|
||||
ownerID string //refers to the owner id of the current instance
|
||||
mapper nameMapper
|
||||
}
|
||||
|
||||
// NewTXTRegistry returns new TXTRegistry object
|
||||
func NewTXTRegistry(provider provider.Provider, txtPrefix, ownerID string) (*TXTRegistry, error) {
|
||||
if ownerID == "" {
|
||||
return nil, errors.New("owner id cannot be empty")
|
||||
}
|
||||
|
||||
mapper := newPrefixNameMapper(txtPrefix)
|
||||
|
||||
return &TXTRegistry{
|
||||
provider: provider,
|
||||
ownerID: ownerID,
|
||||
mapper: mapper,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Records returns the current records from the registry excluding TXT Records
|
||||
// If TXT records was created previously to indicate ownership its corresponding value
|
||||
// will be added to the endpoints Labels map
|
||||
func (im *TXTRegistry) Records(zone string) ([]*endpoint.Endpoint, error) {
|
||||
records, err := im.provider.Records(zone)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
endpoints := make([]*endpoint.Endpoint, 0)
|
||||
|
||||
ownerMap := map[string]string{}
|
||||
|
||||
for _, record := range records {
|
||||
if record.RecordType != "TXT" {
|
||||
endpoints = append(endpoints, record)
|
||||
continue
|
||||
}
|
||||
ownerID := im.extractOwnerID(record.Target)
|
||||
if ownerID == "" {
|
||||
//case when value of txt record cannot be identified
|
||||
//record will not be removed as it will have empty owner
|
||||
endpoints = append(endpoints, record)
|
||||
continue
|
||||
}
|
||||
endpointDNSName := im.mapper.toEndpointName(record.DNSName)
|
||||
ownerMap[endpointDNSName] = ownerID
|
||||
}
|
||||
|
||||
for _, ep := range endpoints {
|
||||
ep.Labels[endpoint.OwnerLabelKey] = ownerMap[ep.DNSName]
|
||||
}
|
||||
|
||||
return endpoints, err
|
||||
}
|
||||
|
||||
// ApplyChanges updates dns provider with the changes
|
||||
// for each created/deleted record it will also take into account TXT records for creation/deletion
|
||||
func (im *TXTRegistry) ApplyChanges(zone string, changes *plan.Changes) error {
|
||||
filteredChanges := &plan.Changes{
|
||||
Create: changes.Create,
|
||||
UpdateNew: filterOwnedRecords(im.ownerID, changes.UpdateNew),
|
||||
UpdateOld: filterOwnedRecords(im.ownerID, changes.UpdateOld),
|
||||
Delete: filterOwnedRecords(im.ownerID, changes.Delete),
|
||||
}
|
||||
|
||||
for _, r := range filteredChanges.Create {
|
||||
txt := endpoint.NewEndpoint(im.mapper.toTXTName(r.DNSName), im.getTXTLabel(), "TXT")
|
||||
filteredChanges.Create = append(filteredChanges.Create, txt)
|
||||
}
|
||||
for _, r := range filteredChanges.Delete {
|
||||
txt := endpoint.NewEndpoint(im.mapper.toTXTName(r.DNSName), im.getTXTLabel(), "TXT")
|
||||
filteredChanges.Delete = append(filteredChanges.Delete, txt)
|
||||
}
|
||||
|
||||
return im.provider.ApplyChanges(zone, filteredChanges)
|
||||
}
|
||||
|
||||
/**
|
||||
TXT registry specific private methods
|
||||
*/
|
||||
|
||||
func (im *TXTRegistry) getTXTLabel() string {
|
||||
return fmt.Sprintf(txtLabelFormat, im.ownerID)
|
||||
}
|
||||
|
||||
func (im *TXTRegistry) extractOwnerID(txtLabel string) string {
|
||||
if matches := txtLabelRegex.FindStringSubmatch(txtLabel); len(matches) == 2 {
|
||||
return matches[1]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
/**
|
||||
nameMapper defines interface which maps the dns name defined for the source
|
||||
to the dns name which TXT record will be created with
|
||||
*/
|
||||
|
||||
type nameMapper interface {
|
||||
toEndpointName(string) string
|
||||
toTXTName(string) string
|
||||
}
|
||||
|
||||
type prefixNameMapper struct {
|
||||
prefix string
|
||||
}
|
||||
|
||||
var _ nameMapper = prefixNameMapper{}
|
||||
|
||||
func newPrefixNameMapper(prefix string) prefixNameMapper {
|
||||
return prefixNameMapper{prefix: prefix}
|
||||
}
|
||||
|
||||
func (pr prefixNameMapper) toEndpointName(txtDNSName string) string {
|
||||
if strings.HasPrefix(txtDNSName, pr.prefix) {
|
||||
return strings.TrimPrefix(txtDNSName, pr.prefix)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (pr prefixNameMapper) toTXTName(endpointDNSName string) string {
|
||||
return pr.prefix + endpointDNSName
|
||||
}
|
384
registry/txt_test.go
Normal file
384
registry/txt_test.go
Normal file
@ -0,0 +1,384 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package registry
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/internal/testutils"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
"github.com/kubernetes-incubator/external-dns/provider"
|
||||
)
|
||||
|
||||
const (
|
||||
testZone = "test-zone.example.com."
|
||||
)
|
||||
|
||||
func TestTXTRegistry(t *testing.T) {
|
||||
t.Run("TestNewTXTRegistry", testTXTRegistryNew)
|
||||
t.Run("TestRecords", testTXTRegistryRecords)
|
||||
t.Run("TestApplyChanges", testTXTRegistryApplyChanges)
|
||||
}
|
||||
|
||||
func testTXTRegistryNew(t *testing.T) {
|
||||
p := provider.NewInMemoryProvider()
|
||||
_, err := NewTXTRegistry(p, "txt", "")
|
||||
if err == nil {
|
||||
t.Fatal("owner should be specified")
|
||||
}
|
||||
|
||||
r, err := NewTXTRegistry(p, "txt", "owner")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, ok := r.mapper.(prefixNameMapper); !ok {
|
||||
t.Error("incorrectly initialized txt registry instance")
|
||||
}
|
||||
if r.ownerID != "owner" || r.provider != p {
|
||||
t.Error("incorrectly initialized txt registry instance")
|
||||
}
|
||||
|
||||
r, err = NewTXTRegistry(p, "", "owner")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, ok := r.mapper.(prefixNameMapper); !ok {
|
||||
t.Error("Incorrect type of prefix name mapper")
|
||||
}
|
||||
|
||||
rs, err := r.Records("random-zone")
|
||||
if err == nil || rs != nil {
|
||||
t.Error("incorrect zone should trigger error")
|
||||
}
|
||||
}
|
||||
|
||||
func testTXTRegistryRecords(t *testing.T) {
|
||||
t.Run("With prefix", testTXTRegistryRecordsPrefixed)
|
||||
t.Run("No prefix", testTXTRegistryRecordsNoPrefix)
|
||||
}
|
||||
|
||||
func testTXTRegistryRecordsPrefixed(t *testing.T) {
|
||||
p := provider.NewInMemoryProvider()
|
||||
p.CreateZone(testZone)
|
||||
p.ApplyChanges(testZone, &plan.Changes{
|
||||
Create: []*endpoint.Endpoint{
|
||||
newEndpointWithOwner("foo.test-zone.example.org", "foo.loadbalancer.com", "CNAME", ""),
|
||||
newEndpointWithOwner("bar.test-zone.example.org", "my-domain.com", "CNAME", ""),
|
||||
newEndpointWithOwner("txt.bar.test-zone.example.org", "\"heritage=external-dns;external-dns/record-owner-id=owner\"", "TXT", ""),
|
||||
newEndpointWithOwner("txt.bar.test-zone.example.org", "baz.test-zone.example.org", "ALIAS", ""),
|
||||
newEndpointWithOwner("qux.test-zone.example.org", "random", "TXT", ""),
|
||||
newEndpointWithOwner("tar.test-zone.example.org", "tar.loadbalancer.com", "ALIAS", ""),
|
||||
newEndpointWithOwner("txt.tar.test-zone.example.org", "\"heritage=external-dns;external-dns/record-owner-id=owner-2\"", "TXT", ""),
|
||||
newEndpointWithOwner("foobar.test-zone.example.org", "foobar.loadbalancer.com", "ALIAS", ""),
|
||||
newEndpointWithOwner("foobar.test-zone.example.org", "\"heritage=external-dns;external-dns/record-owner-id=owner\"", "TXT", ""),
|
||||
},
|
||||
})
|
||||
expectedRecords := []*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "foo.test-zone.example.org",
|
||||
Target: "foo.loadbalancer.com",
|
||||
RecordType: "CNAME",
|
||||
Labels: map[string]string{
|
||||
endpoint.OwnerLabelKey: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
DNSName: "bar.test-zone.example.org",
|
||||
Target: "my-domain.com",
|
||||
RecordType: "CNAME",
|
||||
Labels: map[string]string{
|
||||
endpoint.OwnerLabelKey: "owner",
|
||||
},
|
||||
},
|
||||
{
|
||||
DNSName: "txt.bar.test-zone.example.org",
|
||||
Target: "baz.test-zone.example.org",
|
||||
RecordType: "ALIAS",
|
||||
Labels: map[string]string{
|
||||
endpoint.OwnerLabelKey: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
DNSName: "qux.test-zone.example.org",
|
||||
Target: "random",
|
||||
RecordType: "TXT",
|
||||
Labels: map[string]string{
|
||||
endpoint.OwnerLabelKey: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
DNSName: "tar.test-zone.example.org",
|
||||
Target: "tar.loadbalancer.com",
|
||||
RecordType: "ALIAS",
|
||||
Labels: map[string]string{
|
||||
endpoint.OwnerLabelKey: "owner-2",
|
||||
},
|
||||
},
|
||||
{
|
||||
DNSName: "foobar.test-zone.example.org",
|
||||
Target: "foobar.loadbalancer.com",
|
||||
RecordType: "ALIAS",
|
||||
Labels: map[string]string{
|
||||
endpoint.OwnerLabelKey: "",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
r, _ := NewTXTRegistry(p, "txt.", "owner")
|
||||
records, _ := r.Records(testZone)
|
||||
if !testutils.SameEndpoints(records, expectedRecords) {
|
||||
t.Error("incorrect result returned from txt registry")
|
||||
}
|
||||
}
|
||||
|
||||
func testTXTRegistryRecordsNoPrefix(t *testing.T) {
|
||||
p := provider.NewInMemoryProvider()
|
||||
p.CreateZone(testZone)
|
||||
p.ApplyChanges(testZone, &plan.Changes{
|
||||
Create: []*endpoint.Endpoint{
|
||||
newEndpointWithOwner("foo.test-zone.example.org", "foo.loadbalancer.com", "CNAME", ""),
|
||||
newEndpointWithOwner("bar.test-zone.example.org", "my-domain.com", "CNAME", ""),
|
||||
newEndpointWithOwner("txt.bar.test-zone.example.org", "\"heritage=external-dns;external-dns/record-owner-id=owner\"", "TXT", ""),
|
||||
newEndpointWithOwner("txt.bar.test-zone.example.org", "baz.test-zone.example.org", "ALIAS", ""),
|
||||
newEndpointWithOwner("qux.test-zone.example.org", "random", "TXT", ""),
|
||||
newEndpointWithOwner("tar.test-zone.example.org", "tar.loadbalancer.com", "ALIAS", ""),
|
||||
newEndpointWithOwner("txt.tar.test-zone.example.org", "\"heritage=external-dns;external-dns/record-owner-id=owner-2\"", "TXT", ""),
|
||||
newEndpointWithOwner("foobar.test-zone.example.org", "foobar.loadbalancer.com", "ALIAS", ""),
|
||||
newEndpointWithOwner("foobar.test-zone.example.org", "\"heritage=external-dns;external-dns/record-owner-id=owner\"", "TXT", ""),
|
||||
},
|
||||
})
|
||||
expectedRecords := []*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "foo.test-zone.example.org",
|
||||
Target: "foo.loadbalancer.com",
|
||||
RecordType: "CNAME",
|
||||
Labels: map[string]string{
|
||||
endpoint.OwnerLabelKey: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
DNSName: "bar.test-zone.example.org",
|
||||
Target: "my-domain.com",
|
||||
RecordType: "CNAME",
|
||||
Labels: map[string]string{
|
||||
endpoint.OwnerLabelKey: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
DNSName: "txt.bar.test-zone.example.org",
|
||||
Target: "baz.test-zone.example.org",
|
||||
RecordType: "ALIAS",
|
||||
Labels: map[string]string{
|
||||
endpoint.OwnerLabelKey: "owner",
|
||||
},
|
||||
},
|
||||
{
|
||||
DNSName: "qux.test-zone.example.org",
|
||||
Target: "random",
|
||||
RecordType: "TXT",
|
||||
Labels: map[string]string{
|
||||
endpoint.OwnerLabelKey: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
DNSName: "tar.test-zone.example.org",
|
||||
Target: "tar.loadbalancer.com",
|
||||
RecordType: "ALIAS",
|
||||
Labels: map[string]string{
|
||||
endpoint.OwnerLabelKey: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
DNSName: "foobar.test-zone.example.org",
|
||||
Target: "foobar.loadbalancer.com",
|
||||
RecordType: "ALIAS",
|
||||
Labels: map[string]string{
|
||||
endpoint.OwnerLabelKey: "owner",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
r, _ := NewTXTRegistry(p, "", "owner")
|
||||
records, _ := r.Records(testZone)
|
||||
|
||||
if !testutils.SameEndpoints(records, expectedRecords) {
|
||||
t.Error("incorrect result returned from txt registry")
|
||||
}
|
||||
}
|
||||
|
||||
func testTXTRegistryApplyChanges(t *testing.T) {
|
||||
t.Run("With Prefix", testTXTRegistryApplyChangesWithPrefix)
|
||||
t.Run("No prefix", testTXTRegistryApplyChangesNoPrefix)
|
||||
}
|
||||
|
||||
func testTXTRegistryApplyChangesWithPrefix(t *testing.T) {
|
||||
p := provider.NewInMemoryProvider()
|
||||
p.CreateZone(testZone)
|
||||
p.ApplyChanges(testZone, &plan.Changes{
|
||||
Create: []*endpoint.Endpoint{
|
||||
newEndpointWithOwner("foo.test-zone.example.org", "foo.loadbalancer.com", "CNAME", ""),
|
||||
newEndpointWithOwner("bar.test-zone.example.org", "my-domain.com", "CNAME", ""),
|
||||
newEndpointWithOwner("txt.bar.test-zone.example.org", "\"heritage=external-dns;external-dns/record-owner-id=owner\"", "TXT", ""),
|
||||
newEndpointWithOwner("txt.bar.test-zone.example.org", "baz.test-zone.example.org", "ALIAS", ""),
|
||||
newEndpointWithOwner("qux.test-zone.example.org", "random", "TXT", ""),
|
||||
newEndpointWithOwner("tar.test-zone.example.org", "tar.loadbalancer.com", "ALIAS", ""),
|
||||
newEndpointWithOwner("txt.tar.test-zone.example.org", "\"heritage=external-dns;external-dns/record-owner-id=owner\"", "TXT", ""),
|
||||
newEndpointWithOwner("foobar.test-zone.example.org", "foobar.loadbalancer.com", "ALIAS", ""),
|
||||
newEndpointWithOwner("txt.foobar.test-zone.example.org", "\"heritage=external-dns;external-dns/record-owner-id=owner\"", "TXT", ""),
|
||||
},
|
||||
})
|
||||
r, _ := NewTXTRegistry(p, "txt.", "owner")
|
||||
|
||||
changes := &plan.Changes{
|
||||
Create: []*endpoint.Endpoint{
|
||||
newEndpointWithOwner("new-record-1.test-zone.example.org", "new-loadbalancer-1.lb.com", "", ""),
|
||||
},
|
||||
Delete: []*endpoint.Endpoint{
|
||||
newEndpointWithOwner("foobar.test-zone.example.org", "foobar.loadbalancer.com", "ALIAS", "owner"),
|
||||
},
|
||||
UpdateNew: []*endpoint.Endpoint{
|
||||
newEndpointWithOwner("tar.test-zone.example.org", "new-tar.loadbalancer.com", "ALIAS", "owner"),
|
||||
},
|
||||
UpdateOld: []*endpoint.Endpoint{
|
||||
newEndpointWithOwner("tar.test-zone.example.org", "tar.loadbalancer.com", "ALIAS", "owner"),
|
||||
},
|
||||
}
|
||||
expected := &plan.Changes{
|
||||
Create: []*endpoint.Endpoint{
|
||||
newEndpointWithOwner("new-record-1.test-zone.example.org", "new-loadbalancer-1.lb.com", "", ""),
|
||||
newEndpointWithOwner("txt.new-record-1.test-zone.example.org", "\"heritage=external-dns;external-dns/record-owner-id=owner\"", "TXT", ""),
|
||||
},
|
||||
Delete: []*endpoint.Endpoint{
|
||||
newEndpointWithOwner("foobar.test-zone.example.org", "foobar.loadbalancer.com", "ALIAS", "owner"),
|
||||
newEndpointWithOwner("txt.foobar.test-zone.example.org", "\"heritage=external-dns;external-dns/record-owner-id=owner\"", "TXT", ""),
|
||||
},
|
||||
UpdateNew: []*endpoint.Endpoint{
|
||||
newEndpointWithOwner("tar.test-zone.example.org", "new-tar.loadbalancer.com", "ALIAS", "owner"),
|
||||
},
|
||||
UpdateOld: []*endpoint.Endpoint{
|
||||
newEndpointWithOwner("tar.test-zone.example.org", "tar.loadbalancer.com", "ALIAS", "owner"),
|
||||
},
|
||||
}
|
||||
p.OnApplyChanges = func(got *plan.Changes) {
|
||||
mExpected := map[string][]*endpoint.Endpoint{
|
||||
"Create": expected.Create,
|
||||
"UpdateNew": expected.UpdateNew,
|
||||
"UpdateOld": expected.UpdateOld,
|
||||
"Delete": expected.Delete,
|
||||
}
|
||||
mGot := map[string][]*endpoint.Endpoint{
|
||||
"Create": got.Create,
|
||||
"UpdateNew": got.UpdateNew,
|
||||
"UpdateOld": got.UpdateOld,
|
||||
"Delete": got.Delete,
|
||||
}
|
||||
if !testutils.SamePlanChanges(mGot, mExpected) {
|
||||
t.Error("incorrect plan changes are passed to provider")
|
||||
}
|
||||
}
|
||||
err := r.ApplyChanges(testZone, changes)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
changes = &plan.Changes{}
|
||||
p.OnApplyChanges = func(c *plan.Changes) {}
|
||||
err = r.ApplyChanges("new-zone", changes)
|
||||
if err == nil {
|
||||
t.Error("expected error")
|
||||
}
|
||||
}
|
||||
|
||||
func testTXTRegistryApplyChangesNoPrefix(t *testing.T) {
|
||||
p := provider.NewInMemoryProvider()
|
||||
p.CreateZone(testZone)
|
||||
p.ApplyChanges(testZone, &plan.Changes{
|
||||
Create: []*endpoint.Endpoint{
|
||||
newEndpointWithOwner("foo.test-zone.example.org", "foo.loadbalancer.com", "CNAME", ""),
|
||||
newEndpointWithOwner("bar.test-zone.example.org", "my-domain.com", "CNAME", ""),
|
||||
newEndpointWithOwner("txt.bar.test-zone.example.org", "\"heritage=external-dns;external-dns/record-owner-id=owner\"", "TXT", ""),
|
||||
newEndpointWithOwner("txt.bar.test-zone.example.org", "baz.test-zone.example.org", "ALIAS", ""),
|
||||
newEndpointWithOwner("qux.test-zone.example.org", "random", "TXT", ""),
|
||||
newEndpointWithOwner("tar.test-zone.example.org", "tar.loadbalancer.com", "ALIAS", ""),
|
||||
newEndpointWithOwner("txt.tar.test-zone.example.org", "\"heritage=external-dns;external-dns/record-owner-id=owner\"", "TXT", ""),
|
||||
newEndpointWithOwner("foobar.test-zone.example.org", "foobar.loadbalancer.com", "ALIAS", ""),
|
||||
newEndpointWithOwner("foobar.test-zone.example.org", "\"heritage=external-dns;external-dns/record-owner-id=owner\"", "TXT", ""),
|
||||
},
|
||||
})
|
||||
r, _ := NewTXTRegistry(p, "", "owner")
|
||||
|
||||
changes := &plan.Changes{
|
||||
Create: []*endpoint.Endpoint{
|
||||
newEndpointWithOwner("new-record-1.test-zone.example.org", "new-loadbalancer-1.lb.com", "", ""),
|
||||
},
|
||||
Delete: []*endpoint.Endpoint{
|
||||
newEndpointWithOwner("foobar.test-zone.example.org", "foobar.loadbalancer.com", "ALIAS", "owner"),
|
||||
},
|
||||
UpdateNew: []*endpoint.Endpoint{
|
||||
newEndpointWithOwner("tar.test-zone.example.org", "new-tar.loadbalancer.com", "ALIAS", "owner-2"),
|
||||
},
|
||||
UpdateOld: []*endpoint.Endpoint{
|
||||
newEndpointWithOwner("tar.test-zone.example.org", "tar.loadbalancer.com", "ALIAS", "owner-2"),
|
||||
},
|
||||
}
|
||||
expected := &plan.Changes{
|
||||
Create: []*endpoint.Endpoint{
|
||||
newEndpointWithOwner("new-record-1.test-zone.example.org", "new-loadbalancer-1.lb.com", "", ""),
|
||||
newEndpointWithOwner("new-record-1.test-zone.example.org", "\"heritage=external-dns;external-dns/record-owner-id=owner\"", "TXT", ""),
|
||||
},
|
||||
Delete: []*endpoint.Endpoint{
|
||||
newEndpointWithOwner("foobar.test-zone.example.org", "foobar.loadbalancer.com", "ALIAS", "owner"),
|
||||
newEndpointWithOwner("foobar.test-zone.example.org", "\"heritage=external-dns;external-dns/record-owner-id=owner\"", "TXT", ""),
|
||||
},
|
||||
UpdateNew: []*endpoint.Endpoint{},
|
||||
UpdateOld: []*endpoint.Endpoint{},
|
||||
}
|
||||
p.OnApplyChanges = func(got *plan.Changes) {
|
||||
mExpected := map[string][]*endpoint.Endpoint{
|
||||
"Create": expected.Create,
|
||||
"UpdateNew": expected.UpdateNew,
|
||||
"UpdateOld": expected.UpdateOld,
|
||||
"Delete": expected.Delete,
|
||||
}
|
||||
mGot := map[string][]*endpoint.Endpoint{
|
||||
"Create": got.Create,
|
||||
"UpdateNew": got.UpdateNew,
|
||||
"UpdateOld": got.UpdateOld,
|
||||
"Delete": got.Delete,
|
||||
}
|
||||
if !testutils.SamePlanChanges(mGot, mExpected) {
|
||||
t.Error("incorrect plan changes are passed to provider")
|
||||
}
|
||||
}
|
||||
err := r.ApplyChanges(testZone, changes)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
helper methods
|
||||
|
||||
*/
|
||||
|
||||
func newEndpointWithOwner(dnsName, target, recordType, ownerID string) *endpoint.Endpoint {
|
||||
e := endpoint.NewEndpoint(dnsName, target, recordType)
|
||||
e.Labels[endpoint.OwnerLabelKey] = ownerID
|
||||
return e
|
||||
}
|
@ -46,10 +46,10 @@ func legacyEndpointsFromMateService(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(hostname, lb.IP, ""))
|
||||
}
|
||||
if lb.Hostname != "" {
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint(hostname, lb.Hostname))
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint(hostname, lb.Hostname, ""))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -71,10 +71,10 @@ func endpointsFromIngress(ing *v1beta1.Ingress) []*endpoint.Endpoint {
|
||||
}
|
||||
for _, lb := range ing.Status.LoadBalancer.Ingress {
|
||||
if lb.IP != "" {
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint(rule.Host, lb.IP))
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint(rule.Host, lb.IP, ""))
|
||||
}
|
||||
if lb.Hostname != "" {
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint(rule.Host, lb.Hostname))
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint(rule.Host, lb.Hostname, ""))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -88,10 +88,11 @@ 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))
|
||||
//TODO(ideahitme): consider retrieving record type from resource annotation instead of empty
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint(hostname, lb.IP, ""))
|
||||
}
|
||||
if lb.Hostname != "" {
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint(hostname, lb.Hostname))
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint(hostname, lb.Hostname, ""))
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user