Implementation of multiple targets based on PR #404 and #396 (#418)

* Endpoint.Target is now Endpoint.Targets. This is its own type representing mutliple targets for a single DNS name while adding some convenience for sorting and comparing

* Made everything compile and tests run through with the new Endpoint.Targets

* The ingress source can now properly handle multiple target ips per host

* Added custom conflict resolver, to better understand how conflict resolution has to work for me

* My custom conflict resolver behaves a bit different than the PerResource resolver, therefore I needed to modify the expected test result

Removed unnecessary FIXME

* The ingress source now creates CNAME endpoints with multiple targets to let the DNS provider decide how to handle multiple CNAME targets. This could be interesting for weighted targets etc.

* Adopted the expected results to the new way we create endpoints for CNAMEs

* Removed Add method from Targets since manipulating the slice through here is unnecessary complicated and doesn't deliver enough convenience

* Reverted ConflictResolver to the original one. There is some discussing to do what the best way is to handle conflicts

* Added missing documenting comment to IsLess of Targets

* Added documenting comments to Targets,Targets.Same and NewTargets to clarify their intention and usage

* Service source now also generates endpoints with multiple targets

* Service and Ingress source now sort all Targets for every Endpoint to make order of Targets predictable

* Endpoints generated by the Google Cloud DNS provider now also have sorted Targets to make order of Targets predictable

* Modified provider dyn to be able to compile with multi target changes

* Fixed small nitpicks, so my code is acceptable

* Fixed merge method after updating to new Targets. Replacing '!=' with .Same of course needs a boolean negation

* Tests for dyn provider now also use the new Targets instead of Target

* Simplified extractServiceIps as implied by linki to make it more readable

* ref: change service ClusterIP retrieval again

* Added entry to CHANGELOG.md describing the new features contained in this PR
This commit is contained in:
Till Klocke 2018-02-21 18:11:45 +01:00 committed by Martin Linkhorst
parent f5b0d93e91
commit 5d54849699
38 changed files with 473 additions and 329 deletions

View File

@ -1,5 +1,6 @@
- DigitalOcean: DigitalOcean creates entries with host in them twice (#459) @njuettner - DigitalOcean: DigitalOcean creates entries with host in them twice (#459) @njuettner
- Bugfix: Retrive all DNSimple response pages (#468) @jbowes - Bugfix: Retrive all DNSimple response pages (#468) @jbowes
- external-dns does now provide support for multiple targets for A records. This is currently only supported by the Google Cloud DNS provider (#418) @dereulenspiegel
- Graceful handling of misconfigure password for dyn provider (#470) @jvassev - Graceful handling of misconfigure password for dyn provider (#470) @jvassev
- Don't log sensitive data on start (#463) @jvassev - Don't log sensitive data on start (#463) @jvassev
- Google: Improve logging to help trace misconfigurations (#388) @stealthybox - Google: Improve logging to help trace misconfigurations (#388) @stealthybox

View File

@ -48,25 +48,25 @@ func (p *mockProvider) ApplyChanges(changes *plan.Changes) error {
} }
for i := range changes.Create { for i := range changes.Create {
if changes.Create[i].DNSName != p.ExpectChanges.Create[i].DNSName || changes.Create[i].Target != p.ExpectChanges.Create[i].Target { if changes.Create[i].DNSName != p.ExpectChanges.Create[i].DNSName || !changes.Create[i].Targets.Same(p.ExpectChanges.Create[i].Targets) {
return errors.New("created record is wrong") return errors.New("created record is wrong")
} }
} }
for i := range changes.UpdateNew { for i := range changes.UpdateNew {
if changes.UpdateNew[i].DNSName != p.ExpectChanges.UpdateNew[i].DNSName || changes.UpdateNew[i].Target != p.ExpectChanges.UpdateNew[i].Target { if changes.UpdateNew[i].DNSName != p.ExpectChanges.UpdateNew[i].DNSName || !changes.UpdateNew[i].Targets.Same(p.ExpectChanges.UpdateNew[i].Targets) {
return errors.New("delete record is wrong") return errors.New("delete record is wrong")
} }
} }
for i := range changes.UpdateOld { for i := range changes.UpdateOld {
if changes.UpdateOld[i].DNSName != p.ExpectChanges.UpdateOld[i].DNSName || changes.UpdateOld[i].Target != p.ExpectChanges.UpdateOld[i].Target { if changes.UpdateOld[i].DNSName != p.ExpectChanges.UpdateOld[i].DNSName || !changes.UpdateOld[i].Targets.Same(p.ExpectChanges.UpdateOld[i].Targets) {
return errors.New("delete record is wrong") return errors.New("delete record is wrong")
} }
} }
for i := range changes.Delete { for i := range changes.Delete {
if changes.Delete[i].DNSName != p.ExpectChanges.Delete[i].DNSName || changes.Delete[i].Target != p.ExpectChanges.Delete[i].Target { if changes.Delete[i].DNSName != p.ExpectChanges.Delete[i].DNSName || !changes.Delete[i].Targets.Same(p.ExpectChanges.Delete[i].Targets) {
return errors.New("delete record is wrong") return errors.New("delete record is wrong")
} }
} }
@ -91,11 +91,11 @@ func TestRunOnce(t *testing.T) {
source.On("Endpoints").Return([]*endpoint.Endpoint{ source.On("Endpoints").Return([]*endpoint.Endpoint{
{ {
DNSName: "create-record", DNSName: "create-record",
Target: "1.2.3.4", Targets: endpoint.Targets{"1.2.3.4"},
}, },
{ {
DNSName: "update-record", DNSName: "update-record",
Target: "8.8.4.4", Targets: endpoint.Targets{"8.8.4.4"},
}, },
}, nil) }, nil)
@ -104,25 +104,25 @@ func TestRunOnce(t *testing.T) {
[]*endpoint.Endpoint{ []*endpoint.Endpoint{
{ {
DNSName: "update-record", DNSName: "update-record",
Target: "8.8.8.8", Targets: endpoint.Targets{"8.8.8.8"},
}, },
{ {
DNSName: "delete-record", DNSName: "delete-record",
Target: "4.3.2.1", Targets: endpoint.Targets{"4.3.2.1"},
}, },
}, },
&plan.Changes{ &plan.Changes{
Create: []*endpoint.Endpoint{ Create: []*endpoint.Endpoint{
{DNSName: "create-record", Target: "1.2.3.4"}, {DNSName: "create-record", Targets: endpoint.Targets{"1.2.3.4"}},
}, },
UpdateNew: []*endpoint.Endpoint{ UpdateNew: []*endpoint.Endpoint{
{DNSName: "update-record", Target: "8.8.4.4"}, {DNSName: "update-record", Targets: endpoint.Targets{"8.8.4.4"}},
}, },
UpdateOld: []*endpoint.Endpoint{ UpdateOld: []*endpoint.Endpoint{
{DNSName: "update-record", Target: "8.8.8.8"}, {DNSName: "update-record", Targets: endpoint.Targets{"8.8.8.8"}},
}, },
Delete: []*endpoint.Endpoint{ Delete: []*endpoint.Endpoint{
{DNSName: "delete-record", Target: "4.3.2.1"}, {DNSName: "delete-record", Targets: endpoint.Targets{"4.3.2.1"}},
}, },
}, },
) )

View File

@ -18,6 +18,7 @@ package endpoint
import ( import (
"fmt" "fmt"
"sort"
"strings" "strings"
) )
@ -38,12 +39,78 @@ func (ttl TTL) IsConfigured() bool {
return ttl > 0 return ttl > 0
} }
// Targets is a representation of a list of targets for an endpoint.
type Targets []string
// NewTargets is a convenience method to create a new Targets object from a vararg of strings
func NewTargets(target ...string) Targets {
t := make(Targets, 0, len(target))
t = append(t, target...)
return t
}
func (t Targets) String() string {
return strings.Join(t, ";")
}
func (t Targets) Len() int {
return len(t)
}
func (t Targets) Less(i, j int) bool {
return t[i] < t[j]
}
func (t Targets) Swap(i, j int) {
t[i], t[j] = t[j], t[i]
}
// Same compares to Targets and returns true if they are completely identical
func (t Targets) Same(o Targets) bool {
if len(t) != len(o) {
return false
}
sort.Stable(t)
sort.Stable(o)
for i, e := range t {
if e != o[i] {
return false
}
}
return true
}
// IsLess should fulfill the requirement to compare two targets and chosse the 'lesser' one.
// In the past target was a simple string so simple string comparison could be used. Now we define 'less'
// as either being the shorter list of targets or where the first entry is less.
// FIXME We really need to define under which circumstances a list Targets is considered 'less'
// than another.
func (t Targets) IsLess(o Targets) bool {
if len(t) < len(o) {
return true
}
if len(t) > len(o) {
return false
}
sort.Sort(t)
sort.Sort(o)
for i, e := range t {
if e != o[i] {
return e < o[i]
}
}
return false
}
// Endpoint is a high-level way of a connection between a service and an IP // Endpoint is a high-level way of a connection between a service and an IP
type Endpoint struct { type Endpoint struct {
// The hostname of the DNS record // The hostname of the DNS record
DNSName string DNSName string
// The target the DNS record points to // The targets the DNS record points to
Target string Targets Targets
// RecordType type of record, e.g. CNAME, A, TXT etc // RecordType type of record, e.g. CNAME, A, TXT etc
RecordType string RecordType string
// TTL for the record // TTL for the record
@ -61,7 +128,7 @@ func NewEndpoint(dnsName, target, recordType string) *Endpoint {
func NewEndpointWithTTL(dnsName, target, recordType string, ttl TTL) *Endpoint { func NewEndpointWithTTL(dnsName, target, recordType string, ttl TTL) *Endpoint {
return &Endpoint{ return &Endpoint{
DNSName: strings.TrimSuffix(dnsName, "."), DNSName: strings.TrimSuffix(dnsName, "."),
Target: strings.TrimSuffix(target, "."), Targets: Targets{strings.TrimSuffix(target, ".")},
RecordType: recordType, RecordType: recordType,
Labels: NewLabels(), Labels: NewLabels(),
RecordTTL: ttl, RecordTTL: ttl,
@ -69,5 +136,5 @@ func NewEndpointWithTTL(dnsName, target, recordType string, ttl TTL) *Endpoint {
} }
func (e *Endpoint) String() string { func (e *Endpoint) String() string {
return fmt.Sprintf("%s %d IN %s %s", e.DNSName, e.RecordTTL, e.RecordType, e.Target) return fmt.Sprintf("%s %d IN %s %s", e.DNSName, e.RecordTTL, e.RecordType, e.Targets)
} }

View File

@ -22,7 +22,7 @@ import (
func TestNewEndpoint(t *testing.T) { func TestNewEndpoint(t *testing.T) {
e := NewEndpoint("example.org", "foo.com", "CNAME") e := NewEndpoint("example.org", "foo.com", "CNAME")
if e.DNSName != "example.org" || e.Target != "foo.com" || e.RecordType != "CNAME" { if e.DNSName != "example.org" || e.Targets[0] != "foo.com" || e.RecordType != "CNAME" {
t.Error("endpoint is not initialized correctly") t.Error("endpoint is not initialized correctly")
} }
if e.Labels == nil { if e.Labels == nil {
@ -30,7 +30,7 @@ func TestNewEndpoint(t *testing.T) {
} }
w := NewEndpoint("example.org.", "load-balancer.com.", "") w := NewEndpoint("example.org.", "load-balancer.com.", "")
if w.DNSName != "example.org" || w.Target != "load-balancer.com" || w.RecordType != "" { if w.DNSName != "example.org" || w.Targets[0] != "load-balancer.com" || w.RecordType != "" {
t.Error("endpoint is not initialized correctly") t.Error("endpoint is not initialized correctly")
} }
} }

View File

@ -33,13 +33,12 @@ func (b byAllFields) Less(i, j int) bool {
return true return true
} }
if b[i].DNSName == b[j].DNSName { if b[i].DNSName == b[j].DNSName {
if b[i].Target < b[j].Target { // This rather bad, we need a more complex comparison for Targets, which considers all elements
return true if b[i].Targets.Same(b[j].Targets) {
}
if b[i].Target == b[j].Target {
return b[i].RecordType <= b[j].RecordType return b[i].RecordType <= b[j].RecordType
} }
return false return b[i].Targets.String() <= b[j].Targets.String()
} }
return false return false
} }
@ -47,7 +46,7 @@ func (b byAllFields) Less(i, j int) bool {
// SameEndpoint returns true if two endpoints are same // SameEndpoint returns true if two endpoints are same
// considers example.org. and example.org DNSName/Target as different endpoints // considers example.org. and example.org DNSName/Target as different endpoints
func SameEndpoint(a, b *endpoint.Endpoint) bool { func SameEndpoint(a, b *endpoint.Endpoint) bool {
return a.DNSName == b.DNSName && a.Target == b.Target && a.RecordType == b.RecordType && return a.DNSName == b.DNSName && a.Targets.Same(b.Targets) && a.RecordType == b.RecordType &&
a.Labels[endpoint.OwnerLabelKey] == b.Labels[endpoint.OwnerLabelKey] && a.RecordTTL == b.RecordTTL && a.Labels[endpoint.OwnerLabelKey] == b.Labels[endpoint.OwnerLabelKey] && a.RecordTTL == b.RecordTTL &&
a.Labels[endpoint.ResourceLabelKey] == b.Labels[endpoint.ResourceLabelKey] a.Labels[endpoint.ResourceLabelKey] == b.Labels[endpoint.ResourceLabelKey]
} }

View File

@ -27,31 +27,31 @@ func ExampleSameEndpoints() {
eps := []*endpoint.Endpoint{ eps := []*endpoint.Endpoint{
{ {
DNSName: "example.org", DNSName: "example.org",
Target: "load-balancer.org", Targets: endpoint.Targets{"load-balancer.org"},
}, },
{ {
DNSName: "example.org", DNSName: "example.org",
Target: "load-balancer.org", Targets: endpoint.Targets{"load-balancer.org"},
RecordType: endpoint.RecordTypeTXT, RecordType: endpoint.RecordTypeTXT,
}, },
{ {
DNSName: "abc.com", DNSName: "abc.com",
Target: "something", Targets: endpoint.Targets{"something"},
RecordType: endpoint.RecordTypeTXT, RecordType: endpoint.RecordTypeTXT,
}, },
{ {
DNSName: "abc.com", DNSName: "abc.com",
Target: "1.2.3.4", Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
}, },
{ {
DNSName: "bbc.com", DNSName: "bbc.com",
Target: "foo.com", Targets: endpoint.Targets{"foo.com"},
RecordType: endpoint.RecordTypeCNAME, RecordType: endpoint.RecordTypeCNAME,
}, },
{ {
DNSName: "cbc.com", DNSName: "cbc.com",
Target: "foo.com", Targets: endpoint.Targets{"foo.com"},
RecordType: "CNAME", RecordType: "CNAME",
RecordTTL: endpoint.TTL(60), RecordTTL: endpoint.TTL(60),
}, },

View File

@ -64,7 +64,7 @@ func (s PerResource) ResolveUpdate(current *endpoint.Endpoint, candidates []*end
// less returns true if endpoint x is less than y // less returns true if endpoint x is less than y
func (s PerResource) less(x, y *endpoint.Endpoint) bool { func (s PerResource) less(x, y *endpoint.Endpoint) bool {
return x.Target < y.Target return x.Targets.IsLess(y.Targets)
} }
// TODO: with cross-resource/cross-cluster setup alternative variations of ConflictResolver can be used // TODO: with cross-resource/cross-cluster setup alternative variations of ConflictResolver can be used

View File

@ -45,7 +45,7 @@ func (suite *ResolverSuite) SetupTest() {
// initialize endpoints used in tests // initialize endpoints used in tests
suite.fooV1Cname = &endpoint.Endpoint{ suite.fooV1Cname = &endpoint.Endpoint{
DNSName: "foo", DNSName: "foo",
Target: "v1", Targets: endpoint.Targets{"v1"},
RecordType: "CNAME", RecordType: "CNAME",
Labels: map[string]string{ Labels: map[string]string{
endpoint.ResourceLabelKey: "ingress/default/foo-v1", endpoint.ResourceLabelKey: "ingress/default/foo-v1",
@ -53,7 +53,7 @@ func (suite *ResolverSuite) SetupTest() {
} }
suite.fooV2Cname = &endpoint.Endpoint{ suite.fooV2Cname = &endpoint.Endpoint{
DNSName: "foo", DNSName: "foo",
Target: "v2", Targets: endpoint.Targets{"v2"},
RecordType: "CNAME", RecordType: "CNAME",
Labels: map[string]string{ Labels: map[string]string{
endpoint.ResourceLabelKey: "ingress/default/foo-v2", endpoint.ResourceLabelKey: "ingress/default/foo-v2",
@ -61,7 +61,7 @@ func (suite *ResolverSuite) SetupTest() {
} }
suite.fooV2CnameDuplicate = &endpoint.Endpoint{ suite.fooV2CnameDuplicate = &endpoint.Endpoint{
DNSName: "foo", DNSName: "foo",
Target: "v2", Targets: endpoint.Targets{"v2"},
RecordType: "CNAME", RecordType: "CNAME",
Labels: map[string]string{ Labels: map[string]string{
endpoint.ResourceLabelKey: "ingress/default/foo-v2-duplicate", endpoint.ResourceLabelKey: "ingress/default/foo-v2-duplicate",
@ -69,7 +69,7 @@ func (suite *ResolverSuite) SetupTest() {
} }
suite.fooA5 = &endpoint.Endpoint{ suite.fooA5 = &endpoint.Endpoint{
DNSName: "foo", DNSName: "foo",
Target: "5.5.5.5", Targets: endpoint.Targets{"5.5.5.5"},
RecordType: "A", RecordType: "A",
Labels: map[string]string{ Labels: map[string]string{
endpoint.ResourceLabelKey: "ingress/default/foo-5", endpoint.ResourceLabelKey: "ingress/default/foo-5",
@ -77,7 +77,7 @@ func (suite *ResolverSuite) SetupTest() {
} }
suite.bar127A = &endpoint.Endpoint{ suite.bar127A = &endpoint.Endpoint{
DNSName: "bar", DNSName: "bar",
Target: "127.0.0.1", Targets: endpoint.Targets{"127.0.0.1"},
RecordType: "A", RecordType: "A",
Labels: map[string]string{ Labels: map[string]string{
endpoint.ResourceLabelKey: "ingress/default/bar-127", endpoint.ResourceLabelKey: "ingress/default/bar-127",
@ -85,7 +85,7 @@ func (suite *ResolverSuite) SetupTest() {
} }
suite.bar127AAnother = &endpoint.Endpoint{ //TODO: remove this once we move to multiple targets under same endpoint suite.bar127AAnother = &endpoint.Endpoint{ //TODO: remove this once we move to multiple targets under same endpoint
DNSName: "bar", DNSName: "bar",
Target: "8.8.8.8", Targets: endpoint.Targets{"8.8.8.8"},
RecordType: "A", RecordType: "A",
Labels: map[string]string{ Labels: map[string]string{
endpoint.ResourceLabelKey: "ingress/default/bar-127", endpoint.ResourceLabelKey: "ingress/default/bar-127",
@ -93,7 +93,7 @@ func (suite *ResolverSuite) SetupTest() {
} }
suite.bar192A = &endpoint.Endpoint{ suite.bar192A = &endpoint.Endpoint{
DNSName: "bar", DNSName: "bar",
Target: "192.168.0.1", Targets: endpoint.Targets{"192.168.0.1"},
RecordType: "A", RecordType: "A",
Labels: map[string]string{ Labels: map[string]string{
endpoint.ResourceLabelKey: "ingress/default/bar-192", endpoint.ResourceLabelKey: "ingress/default/bar-192",
@ -101,7 +101,7 @@ func (suite *ResolverSuite) SetupTest() {
} }
suite.legacyBar192A = &endpoint.Endpoint{ suite.legacyBar192A = &endpoint.Endpoint{
DNSName: "bar", DNSName: "bar",
Target: "192.168.0.1", Targets: endpoint.Targets{"192.168.0.1"},
RecordType: "A", RecordType: "A",
} }
} }
@ -120,7 +120,7 @@ func (suite *ResolverSuite) TestStrictResolver() {
// should actually get the updated record (note ttl is different) // should actually get the updated record (note ttl is different)
newFooV1Cname := &endpoint.Endpoint{ newFooV1Cname := &endpoint.Endpoint{
DNSName: suite.fooV1Cname.DNSName, DNSName: suite.fooV1Cname.DNSName,
Target: suite.fooV1Cname.Target, Targets: suite.fooV1Cname.Targets,
Labels: suite.fooV1Cname.Labels, Labels: suite.fooV1Cname.Labels,
RecordType: suite.fooV1Cname.RecordType, RecordType: suite.fooV1Cname.RecordType,
RecordTTL: suite.fooV1Cname.RecordTTL + 1, // ttl is different RecordTTL: suite.fooV1Cname.RecordTTL + 1, // ttl is different

View File

@ -166,7 +166,7 @@ func inheritOwner(from, to *endpoint.Endpoint) {
} }
func targetChanged(desired, current *endpoint.Endpoint) bool { func targetChanged(desired, current *endpoint.Endpoint) bool {
return desired.Target != current.Target return !desired.Targets.Same(current.Targets)
} }
func shouldUpdateTTL(desired, current *endpoint.Endpoint) bool { func shouldUpdateTTL(desired, current *endpoint.Endpoint) bool {

View File

@ -39,7 +39,7 @@ type PlanTestSuite struct {
func (suite *PlanTestSuite) SetupTest() { func (suite *PlanTestSuite) SetupTest() {
suite.fooV1Cname = &endpoint.Endpoint{ suite.fooV1Cname = &endpoint.Endpoint{
DNSName: "foo", DNSName: "foo",
Target: "v1", Targets: endpoint.Targets{"v1"},
RecordType: "CNAME", RecordType: "CNAME",
Labels: map[string]string{ Labels: map[string]string{
endpoint.ResourceLabelKey: "ingress/default/foo-v1", endpoint.ResourceLabelKey: "ingress/default/foo-v1",
@ -49,7 +49,7 @@ func (suite *PlanTestSuite) SetupTest() {
// same resource as fooV1Cname, but target is different. It will never be picked because its target lexicographically bigger than "v1" // same resource as fooV1Cname, but target is different. It will never be picked because its target lexicographically bigger than "v1"
suite.fooV3CnameSameResource = &endpoint.Endpoint{ // TODO: remove this once endpoint can support multiple targets suite.fooV3CnameSameResource = &endpoint.Endpoint{ // TODO: remove this once endpoint can support multiple targets
DNSName: "foo", DNSName: "foo",
Target: "v3", Targets: endpoint.Targets{"v3"},
RecordType: "CNAME", RecordType: "CNAME",
Labels: map[string]string{ Labels: map[string]string{
endpoint.ResourceLabelKey: "ingress/default/foo-v1", endpoint.ResourceLabelKey: "ingress/default/foo-v1",
@ -58,7 +58,7 @@ func (suite *PlanTestSuite) SetupTest() {
} }
suite.fooV2Cname = &endpoint.Endpoint{ suite.fooV2Cname = &endpoint.Endpoint{
DNSName: "foo", DNSName: "foo",
Target: "v2", Targets: endpoint.Targets{"v2"},
RecordType: "CNAME", RecordType: "CNAME",
Labels: map[string]string{ Labels: map[string]string{
endpoint.ResourceLabelKey: "ingress/default/foo-v2", endpoint.ResourceLabelKey: "ingress/default/foo-v2",
@ -66,12 +66,12 @@ func (suite *PlanTestSuite) SetupTest() {
} }
suite.fooV2CnameNoLabel = &endpoint.Endpoint{ suite.fooV2CnameNoLabel = &endpoint.Endpoint{
DNSName: "foo", DNSName: "foo",
Target: "v2", Targets: endpoint.Targets{"v2"},
RecordType: "CNAME", RecordType: "CNAME",
} }
suite.fooA5 = &endpoint.Endpoint{ suite.fooA5 = &endpoint.Endpoint{
DNSName: "foo", DNSName: "foo",
Target: "5.5.5.5", Targets: endpoint.Targets{"5.5.5.5"},
RecordType: "A", RecordType: "A",
Labels: map[string]string{ Labels: map[string]string{
endpoint.ResourceLabelKey: "ingress/default/foo-5", endpoint.ResourceLabelKey: "ingress/default/foo-5",
@ -79,7 +79,7 @@ func (suite *PlanTestSuite) SetupTest() {
} }
suite.bar127A = &endpoint.Endpoint{ suite.bar127A = &endpoint.Endpoint{
DNSName: "bar", DNSName: "bar",
Target: "127.0.0.1", Targets: endpoint.Targets{"127.0.0.1"},
RecordType: "A", RecordType: "A",
Labels: map[string]string{ Labels: map[string]string{
endpoint.ResourceLabelKey: "ingress/default/bar-127", endpoint.ResourceLabelKey: "ingress/default/bar-127",
@ -87,7 +87,7 @@ func (suite *PlanTestSuite) SetupTest() {
} }
suite.bar127AWithTTL = &endpoint.Endpoint{ suite.bar127AWithTTL = &endpoint.Endpoint{
DNSName: "bar", DNSName: "bar",
Target: "127.0.0.1", Targets: endpoint.Targets{"127.0.0.1"},
RecordType: "A", RecordType: "A",
RecordTTL: 300, RecordTTL: 300,
Labels: map[string]string{ Labels: map[string]string{
@ -96,7 +96,7 @@ func (suite *PlanTestSuite) SetupTest() {
} }
suite.bar192A = &endpoint.Endpoint{ suite.bar192A = &endpoint.Endpoint{
DNSName: "bar", DNSName: "bar",
Target: "192.168.0.1", Targets: endpoint.Targets{"192.168.0.1"},
RecordType: "A", RecordType: "A",
Labels: map[string]string{ Labels: map[string]string{
endpoint.ResourceLabelKey: "ingress/default/bar-192", endpoint.ResourceLabelKey: "ingress/default/bar-192",
@ -196,7 +196,7 @@ func (suite *PlanTestSuite) TestSyncSecondRoundWithOwnerInherited() {
expectedUpdateOld := []*endpoint.Endpoint{suite.fooV1Cname} expectedUpdateOld := []*endpoint.Endpoint{suite.fooV1Cname}
expectedUpdateNew := []*endpoint.Endpoint{{ expectedUpdateNew := []*endpoint.Endpoint{{
DNSName: suite.fooV2Cname.DNSName, DNSName: suite.fooV2Cname.DNSName,
Target: suite.fooV2Cname.Target, Targets: suite.fooV2Cname.Targets,
RecordType: suite.fooV2Cname.RecordType, RecordType: suite.fooV2Cname.RecordType,
RecordTTL: suite.fooV2Cname.RecordTTL, RecordTTL: suite.fooV2Cname.RecordTTL,
Labels: map[string]string{ Labels: map[string]string{

View File

@ -28,12 +28,12 @@ func TestApply(t *testing.T) {
// empty list of records // empty list of records
empty := []*endpoint.Endpoint{} empty := []*endpoint.Endpoint{}
// a simple entry // a simple entry
fooV1 := []*endpoint.Endpoint{{DNSName: "foo", Target: "v1"}} fooV1 := []*endpoint.Endpoint{{DNSName: "foo", Targets: endpoint.Targets{"v1"}}}
// the same entry but with different target // the same entry but with different target
fooV2 := []*endpoint.Endpoint{{DNSName: "foo", Target: "v2"}} fooV2 := []*endpoint.Endpoint{{DNSName: "foo", Targets: endpoint.Targets{"v2"}}}
// another two simple entries // another two simple entries
bar := []*endpoint.Endpoint{{DNSName: "bar", Target: "v1"}} bar := []*endpoint.Endpoint{{DNSName: "bar", Targets: endpoint.Targets{"v1"}}}
baz := []*endpoint.Endpoint{{DNSName: "baz", Target: "v1"}} baz := []*endpoint.Endpoint{{DNSName: "baz", Targets: endpoint.Targets{"v1"}}}
for _, tc := range []struct { for _, tc := range []struct {
policy Policy policy Policy

View File

@ -378,8 +378,8 @@ func newChange(action string, endpoint *endpoint.Endpoint) *route53.Change {
if isAWSLoadBalancer(endpoint) { if isAWSLoadBalancer(endpoint) {
change.ResourceRecordSet.Type = aws.String(route53.RRTypeA) change.ResourceRecordSet.Type = aws.String(route53.RRTypeA)
change.ResourceRecordSet.AliasTarget = &route53.AliasTarget{ change.ResourceRecordSet.AliasTarget = &route53.AliasTarget{
DNSName: aws.String(endpoint.Target), DNSName: aws.String(endpoint.Targets[0]),
HostedZoneId: aws.String(canonicalHostedZone(endpoint.Target)), HostedZoneId: aws.String(canonicalHostedZone(endpoint.Targets[0])),
EvaluateTargetHealth: aws.Bool(evaluateTargetHealth), EvaluateTargetHealth: aws.Bool(evaluateTargetHealth),
} }
} else { } else {
@ -391,7 +391,7 @@ func newChange(action string, endpoint *endpoint.Endpoint) *route53.Change {
} }
change.ResourceRecordSet.ResourceRecords = []*route53.ResourceRecord{ change.ResourceRecordSet.ResourceRecords = []*route53.ResourceRecord{
{ {
Value: aws.String(endpoint.Target), Value: aws.String(endpoint.Targets[0]),
}, },
} }
} }
@ -429,7 +429,7 @@ func suitableZones(hostname string, zones map[string]*route53.HostedZone) []*rou
// isAWSLoadBalancer determines if a given hostname belongs to an AWS load balancer. // isAWSLoadBalancer determines if a given hostname belongs to an AWS load balancer.
func isAWSLoadBalancer(ep *endpoint.Endpoint) bool { func isAWSLoadBalancer(ep *endpoint.Endpoint) bool {
if ep.RecordType == endpoint.RecordTypeCNAME { if ep.RecordType == endpoint.RecordTypeCNAME {
return canonicalHostedZone(ep.Target) != "" return canonicalHostedZone(ep.Targets[0]) != ""
} }
return false return false

View File

@ -630,7 +630,7 @@ func TestAWSCreateRecordsWithCNAME(t *testing.T) {
provider := newAWSProvider(t, NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), NewZoneIDFilter([]string{}), NewZoneTypeFilter(""), false, []*endpoint.Endpoint{}) provider := newAWSProvider(t, NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), NewZoneIDFilter([]string{}), NewZoneTypeFilter(""), false, []*endpoint.Endpoint{})
records := []*endpoint.Endpoint{ records := []*endpoint.Endpoint{
{DNSName: "create-test.zone-1.ext-dns-test-2.teapot.zalan.do", Target: "foo.example.org", RecordType: endpoint.RecordTypeCNAME}, {DNSName: "create-test.zone-1.ext-dns-test-2.teapot.zalan.do", Targets: endpoint.Targets{"foo.example.org"}, RecordType: endpoint.RecordTypeCNAME},
} }
require.NoError(t, provider.CreateRecords(records)) require.NoError(t, provider.CreateRecords(records))
@ -655,7 +655,7 @@ func TestAWSCreateRecordsWithALIAS(t *testing.T) {
provider := newAWSProvider(t, NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), NewZoneIDFilter([]string{}), NewZoneTypeFilter(""), false, []*endpoint.Endpoint{}) provider := newAWSProvider(t, NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), NewZoneIDFilter([]string{}), NewZoneTypeFilter(""), false, []*endpoint.Endpoint{})
records := []*endpoint.Endpoint{ records := []*endpoint.Endpoint{
{DNSName: "create-test.zone-1.ext-dns-test-2.teapot.zalan.do", Target: "foo.eu-central-1.elb.amazonaws.com", RecordType: endpoint.RecordTypeCNAME}, {DNSName: "create-test.zone-1.ext-dns-test-2.teapot.zalan.do", Targets: endpoint.Targets{"foo.eu-central-1.elb.amazonaws.com"}, RecordType: endpoint.RecordTypeCNAME},
} }
require.NoError(t, provider.CreateRecords(records)) require.NoError(t, provider.CreateRecords(records))
@ -685,7 +685,7 @@ func TestAWSisLoadBalancer(t *testing.T) {
{"foo.example.org", endpoint.RecordTypeCNAME, false}, {"foo.example.org", endpoint.RecordTypeCNAME, false},
} { } {
ep := &endpoint.Endpoint{ ep := &endpoint.Endpoint{
Target: tc.target, Targets: endpoint.Targets{tc.target},
RecordType: tc.recordType, RecordType: tc.recordType,
} }
assert.Equal(t, tc.expected, isAWSLoadBalancer(ep)) assert.Equal(t, tc.expected, isAWSLoadBalancer(ep))

View File

@ -163,7 +163,7 @@ func (p *AzureProvider) Records() (endpoints []*endpoint.Endpoint, _ error) {
"Found %s record for '%s' with target '%s'.", "Found %s record for '%s' with target '%s'.",
ep.RecordType, ep.RecordType,
ep.DNSName, ep.DNSName,
ep.Target, ep.Targets,
) )
endpoints = append(endpoints, ep) endpoints = append(endpoints, ep)
return true return true
@ -323,7 +323,7 @@ func (p *AzureProvider) updateRecords(updated azureChangeMap) {
"Would update %s record named '%s' to '%s' for Azure DNS zone '%s'.", "Would update %s record named '%s' to '%s' for Azure DNS zone '%s'.",
endpoint.RecordType, endpoint.RecordType,
name, name,
endpoint.Target, endpoint.Targets,
zone, zone,
) )
continue continue
@ -333,7 +333,7 @@ func (p *AzureProvider) updateRecords(updated azureChangeMap) {
"Updating %s record named '%s' to '%s' for Azure DNS zone '%s'.", "Updating %s record named '%s' to '%s' for Azure DNS zone '%s'.",
endpoint.RecordType, endpoint.RecordType,
name, name,
endpoint.Target, endpoint.Targets,
zone, zone,
) )
@ -354,7 +354,7 @@ func (p *AzureProvider) updateRecords(updated azureChangeMap) {
"Failed to update %s record named '%s' to '%s' for DNS zone '%s': %v", "Failed to update %s record named '%s' to '%s' for DNS zone '%s': %v",
endpoint.RecordType, endpoint.RecordType,
name, name,
endpoint.Target, endpoint.Targets,
zone, zone,
err, err,
) )
@ -388,7 +388,7 @@ func (p *AzureProvider) newRecordSet(endpoint *endpoint.Endpoint) (dns.RecordSet
TTL: to.Int64Ptr(ttl), TTL: to.Int64Ptr(ttl),
ARecords: &[]dns.ARecord{ ARecords: &[]dns.ARecord{
{ {
Ipv4Address: to.StringPtr(endpoint.Target), Ipv4Address: to.StringPtr(endpoint.Targets[0]),
}, },
}, },
}, },
@ -398,7 +398,7 @@ func (p *AzureProvider) newRecordSet(endpoint *endpoint.Endpoint) (dns.RecordSet
RecordSetProperties: &dns.RecordSetProperties{ RecordSetProperties: &dns.RecordSetProperties{
TTL: to.Int64Ptr(ttl), TTL: to.Int64Ptr(ttl),
CnameRecord: &dns.CnameRecord{ CnameRecord: &dns.CnameRecord{
Cname: to.StringPtr(endpoint.Target), Cname: to.StringPtr(endpoint.Targets[0]),
}, },
}, },
}, nil }, nil
@ -409,7 +409,7 @@ func (p *AzureProvider) newRecordSet(endpoint *endpoint.Endpoint) (dns.RecordSet
TxtRecords: &[]dns.TxtRecord{ TxtRecords: &[]dns.TxtRecord{
{ {
Value: &[]string{ Value: &[]string{
endpoint.Target, endpoint.Targets[0],
}, },
}, },
}, },

View File

@ -290,7 +290,7 @@ func newCloudFlareChange(action string, endpoint *endpoint.Endpoint, proxied boo
TTL: 1, TTL: 1,
Proxied: proxied, Proxied: proxied,
Type: endpoint.RecordType, Type: endpoint.RecordType,
Content: endpoint.Target, Content: endpoint.Targets[0],
}, },
} }
} }

View File

@ -21,11 +21,11 @@ import (
"os" "os"
"testing" "testing"
"github.com/cloudflare/cloudflare-go"
"github.com/kubernetes-incubator/external-dns/endpoint" "github.com/kubernetes-incubator/external-dns/endpoint"
"github.com/kubernetes-incubator/external-dns/plan" "github.com/kubernetes-incubator/external-dns/plan"
cloudflare "github.com/cloudflare/cloudflare-go"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -336,12 +336,12 @@ func (m *mockCloudFlareUpdateRecordsFail) ListZones(zoneID ...string) ([]cloudfl
} }
func TestNewCloudFlareChanges(t *testing.T) { func TestNewCloudFlareChanges(t *testing.T) {
endpoints := []*endpoint.Endpoint{{DNSName: "new", Target: "target"}} endpoints := []*endpoint.Endpoint{{DNSName: "new", Targets: endpoint.Targets{"target"}}}
newCloudFlareChanges(cloudFlareCreate, endpoints, true) newCloudFlareChanges(cloudFlareCreate, endpoints, true)
} }
func TestNewCloudFlareChangeNoProxied(t *testing.T) { func TestNewCloudFlareChangeNoProxied(t *testing.T) {
change := newCloudFlareChange(cloudFlareCreate, &endpoint.Endpoint{DNSName: "new", RecordType: "A", Target: "target"}, false) change := newCloudFlareChange(cloudFlareCreate, &endpoint.Endpoint{DNSName: "new", RecordType: "A", Targets: endpoint.Targets{"target"}}, false)
assert.False(t, change.ResourceRecordSet.Proxied) assert.False(t, change.ResourceRecordSet.Proxied)
} }
@ -361,7 +361,7 @@ func TestNewCloudFlareChangeProxiable(t *testing.T) {
} }
for _, cloudFlareType := range cloudFlareTypes { for _, cloudFlareType := range cloudFlareTypes {
change := newCloudFlareChange(cloudFlareCreate, &endpoint.Endpoint{DNSName: "new", RecordType: cloudFlareType.recordType, Target: "target"}, true) change := newCloudFlareChange(cloudFlareCreate, &endpoint.Endpoint{DNSName: "new", RecordType: cloudFlareType.recordType, Targets: endpoint.Targets{"target"}}, true)
if cloudFlareType.proxiable { if cloudFlareType.proxiable {
assert.True(t, change.ResourceRecordSet.Proxied) assert.True(t, change.ResourceRecordSet.Proxied)
@ -370,7 +370,7 @@ func TestNewCloudFlareChangeProxiable(t *testing.T) {
} }
} }
change := newCloudFlareChange(cloudFlareCreate, &endpoint.Endpoint{DNSName: "*.foo", RecordType: "A", Target: "target"}, true) change := newCloudFlareChange(cloudFlareCreate, &endpoint.Endpoint{DNSName: "*.foo", RecordType: "A", Targets: endpoint.Targets{"target"}}, true)
assert.False(t, change.ResourceRecordSet.Proxied) assert.False(t, change.ResourceRecordSet.Proxied)
} }
@ -433,10 +433,10 @@ func TestApplyChanges(t *testing.T) {
provider := &CloudFlareProvider{ provider := &CloudFlareProvider{
Client: &mockCloudFlareClient{}, Client: &mockCloudFlareClient{},
} }
changes.Create = []*endpoint.Endpoint{{DNSName: "new.ext-dns-test.zalando.to.", Target: "target"}, {DNSName: "new.ext-dns-test.unrelated.to.", Target: "target"}} changes.Create = []*endpoint.Endpoint{{DNSName: "new.ext-dns-test.zalando.to.", Targets: endpoint.Targets{"target"}}, {DNSName: "new.ext-dns-test.unrelated.to.", Targets: endpoint.Targets{"target"}}}
changes.Delete = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test.zalando.to.", Target: "target"}} changes.Delete = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test.zalando.to.", Targets: endpoint.Targets{"target"}}}
changes.UpdateOld = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test.zalando.to.", Target: "target-old"}} changes.UpdateOld = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test.zalando.to.", Targets: endpoint.Targets{"target-old"}}}
changes.UpdateNew = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test.zalando.to.", Target: "target-new"}} changes.UpdateNew = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test.zalando.to.", Targets: endpoint.Targets{"target-new"}}}
err := provider.ApplyChanges(changes) err := provider.ApplyChanges(changes)
if err != nil { if err != nil {
t.Errorf("should not fail, %s", err) t.Errorf("should not fail, %s", err)

View File

@ -260,7 +260,7 @@ func newDigitalOceanChange(action string, endpoint *endpoint.Endpoint) *DigitalO
ResourceRecordSet: godo.DomainRecord{ ResourceRecordSet: godo.DomainRecord{
Name: endpoint.DNSName, Name: endpoint.DNSName,
Type: endpoint.RecordType, Type: endpoint.RecordType,
Data: endpoint.Target, Data: endpoint.Targets[0],
}, },
} }
return change return change

View File

@ -400,7 +400,7 @@ func (m *mockDigitalOceanCreateRecordsFail) Records(ctx context.Context, domain
func TestNewDigitalOceanChanges(t *testing.T) { func TestNewDigitalOceanChanges(t *testing.T) {
action := DigitalOceanCreate action := DigitalOceanCreate
endpoints := []*endpoint.Endpoint{{DNSName: "new", Target: "target"}} endpoints := []*endpoint.Endpoint{{DNSName: "new", Targets: endpoint.Targets{"target"}}}
_ = newDigitalOceanChanges(action, endpoints) _ = newDigitalOceanChanges(action, endpoints)
} }
@ -425,10 +425,10 @@ func TestDigitalOceanApplyChanges(t *testing.T) {
provider := &DigitalOceanProvider{ provider := &DigitalOceanProvider{
Client: &mockDigitalOceanClient{}, Client: &mockDigitalOceanClient{},
} }
changes.Create = []*endpoint.Endpoint{{DNSName: "new.ext-dns-test.bar.com", Target: "target"}, {DNSName: "new.ext-dns-test.unexpected.com", Target: "target"}} changes.Create = []*endpoint.Endpoint{{DNSName: "new.ext-dns-test.bar.com", Targets: endpoint.Targets{"target"}}, {DNSName: "new.ext-dns-test.unexpected.com", Targets: endpoint.Targets{"target"}}}
changes.Delete = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test.bar.com", Target: "target"}} changes.Delete = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test.bar.com", Targets: endpoint.Targets{"target"}}}
changes.UpdateOld = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test.bar.de", Target: "target-old"}} changes.UpdateOld = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test.bar.de", Targets: endpoint.Targets{"target-old"}}}
changes.UpdateNew = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test.foo.com", Target: "target-new"}} changes.UpdateNew = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test.foo.com", Targets: endpoint.Targets{"target-new"}}}
err := provider.ApplyChanges(changes) err := provider.ApplyChanges(changes)
if err != nil { if err != nil {
t.Errorf("should not fail, %s", err) t.Errorf("should not fail, %s", err)

View File

@ -192,7 +192,7 @@ func newDnsimpleChange(action string, e *endpoint.Endpoint) *dnsimpleChange {
ResourceRecordSet: dnsimple.ZoneRecord{ ResourceRecordSet: dnsimple.ZoneRecord{
Name: e.DNSName, Name: e.DNSName,
Type: e.RecordType, Type: e.RecordType,
Content: e.Target, Content: e.Targets[0],
}, },
} }
return change return change

View File

@ -138,9 +138,9 @@ func testDnsimpleProviderRecords(t *testing.T) {
} }
func testDnsimpleProviderApplyChanges(t *testing.T) { func testDnsimpleProviderApplyChanges(t *testing.T) {
changes := &plan.Changes{} changes := &plan.Changes{}
changes.Create = []*endpoint.Endpoint{{DNSName: "example.example.com", Target: "target", RecordType: endpoint.RecordTypeCNAME}} changes.Create = []*endpoint.Endpoint{{DNSName: "example.example.com", Targets: endpoint.Targets{"target"}, RecordType: endpoint.RecordTypeCNAME}}
changes.Delete = []*endpoint.Endpoint{{DNSName: "example-beta.example.com", Target: "127.0.0.1", RecordType: endpoint.RecordTypeA}} changes.Delete = []*endpoint.Endpoint{{DNSName: "example-beta.example.com", Targets: endpoint.Targets{"127.0.0.1"}, RecordType: endpoint.RecordTypeA}}
changes.UpdateNew = []*endpoint.Endpoint{{DNSName: "example.example.com", Target: "target", RecordType: endpoint.RecordTypeCNAME}} changes.UpdateNew = []*endpoint.Endpoint{{DNSName: "example.example.com", Targets: endpoint.Targets{"target"}, RecordType: endpoint.RecordTypeCNAME}}
mockProvider.accountID = "1" mockProvider.accountID = "1"
err := mockProvider.ApplyChanges(changes) err := mockProvider.ApplyChanges(changes)

View File

@ -214,7 +214,7 @@ func merge(updateOld, updateNew []*endpoint.Endpoint) []*endpoint.Endpoint {
continue continue
} }
if matchingNew.Target != old.Target { if !matchingNew.Targets.Same(old.Targets) {
// new target: always update, TTL will be overwritten too if necessary // new target: always update, TTL will be overwritten too if necessary
result = append(result, matchingNew) result = append(result, matchingNew)
continue continue
@ -275,7 +275,7 @@ func (d *dynProviderState) recordLinkToEndpoint(client *dynect.Client, recordLin
DNSName: rec.Data.FQDN, DNSName: rec.Data.FQDN,
RecordTTL: endpoint.TTL(rec.Data.TTL), RecordTTL: endpoint.TTL(rec.Data.TTL),
RecordType: rec.Data.RecordType, RecordType: rec.Data.RecordType,
Target: target, Targets: endpoint.Targets{target},
} }
log.Debugf("Fetched new endpoint for %s: %+v", recordLink, result) log.Debugf("Fetched new endpoint for %s: %+v", recordLink, result)
@ -297,11 +297,11 @@ func endpointToRecord(ep *endpoint.Endpoint) *dynect.DataBlock {
result := dynect.DataBlock{} result := dynect.DataBlock{}
if ep.RecordType == endpoint.RecordTypeA { if ep.RecordType == endpoint.RecordTypeA {
result.Address = ep.Target result.Address = ep.Targets[0]
} else if ep.RecordType == endpoint.RecordTypeCNAME { } else if ep.RecordType == endpoint.RecordTypeCNAME {
result.CName = ep.Target result.CName = ep.Targets[0]
} else if ep.RecordType == endpoint.RecordTypeTXT { } else if ep.RecordType == endpoint.RecordTypeTXT {
result.TxtData = ep.Target result.TxtData = ep.Targets[0]
} }
return &result return &result

View File

@ -32,13 +32,13 @@ func TestDynMerge_NoUpdateOnTTL0Changes(t *testing.T) {
updateOld := []*endpoint.Endpoint{ updateOld := []*endpoint.Endpoint{
{ {
DNSName: "name1", DNSName: "name1",
Target: "target1", Targets: endpoint.Targets{"target1"},
RecordTTL: endpoint.TTL(1), RecordTTL: endpoint.TTL(1),
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
}, },
{ {
DNSName: "name2", DNSName: "name2",
Target: "target2", Targets: endpoint.Targets{"target2"},
RecordTTL: endpoint.TTL(1), RecordTTL: endpoint.TTL(1),
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
}, },
@ -47,13 +47,13 @@ func TestDynMerge_NoUpdateOnTTL0Changes(t *testing.T) {
updateNew := []*endpoint.Endpoint{ updateNew := []*endpoint.Endpoint{
{ {
DNSName: "name1", DNSName: "name1",
Target: "target1", Targets: endpoint.Targets{"target1"},
RecordTTL: endpoint.TTL(0), RecordTTL: endpoint.TTL(0),
RecordType: endpoint.RecordTypeCNAME, RecordType: endpoint.RecordTypeCNAME,
}, },
{ {
DNSName: "name2", DNSName: "name2",
Target: "target2", Targets: endpoint.Targets{"target2"},
RecordTTL: endpoint.TTL(0), RecordTTL: endpoint.TTL(0),
RecordType: endpoint.RecordTypeCNAME, RecordType: endpoint.RecordTypeCNAME,
}, },
@ -66,13 +66,13 @@ func TestDynMerge_UpdateOnTTLChanges(t *testing.T) {
updateOld := []*endpoint.Endpoint{ updateOld := []*endpoint.Endpoint{
{ {
DNSName: "name1", DNSName: "name1",
Target: "target1", Targets: endpoint.Targets{"target1"},
RecordTTL: endpoint.TTL(1), RecordTTL: endpoint.TTL(1),
RecordType: endpoint.RecordTypeCNAME, RecordType: endpoint.RecordTypeCNAME,
}, },
{ {
DNSName: "name2", DNSName: "name2",
Target: "target2", Targets: endpoint.Targets{"target2"},
RecordTTL: endpoint.TTL(1), RecordTTL: endpoint.TTL(1),
RecordType: endpoint.RecordTypeCNAME, RecordType: endpoint.RecordTypeCNAME,
}, },
@ -81,13 +81,13 @@ func TestDynMerge_UpdateOnTTLChanges(t *testing.T) {
updateNew := []*endpoint.Endpoint{ updateNew := []*endpoint.Endpoint{
{ {
DNSName: "name1", DNSName: "name1",
Target: "target1", Targets: endpoint.Targets{"target1"},
RecordTTL: endpoint.TTL(77), RecordTTL: endpoint.TTL(77),
RecordType: endpoint.RecordTypeCNAME, RecordType: endpoint.RecordTypeCNAME,
}, },
{ {
DNSName: "name2", DNSName: "name2",
Target: "target2", Targets: endpoint.Targets{"target2"},
RecordTTL: endpoint.TTL(10), RecordTTL: endpoint.TTL(10),
RecordType: endpoint.RecordTypeCNAME, RecordType: endpoint.RecordTypeCNAME,
}, },
@ -102,13 +102,13 @@ func TestDynMerge_AlwaysUpdateTarget(t *testing.T) {
updateOld := []*endpoint.Endpoint{ updateOld := []*endpoint.Endpoint{
{ {
DNSName: "name1", DNSName: "name1",
Target: "target1", Targets: endpoint.Targets{"target1"},
RecordTTL: endpoint.TTL(1), RecordTTL: endpoint.TTL(1),
RecordType: endpoint.RecordTypeCNAME, RecordType: endpoint.RecordTypeCNAME,
}, },
{ {
DNSName: "name2", DNSName: "name2",
Target: "target2", Targets: endpoint.Targets{"target2"},
RecordTTL: endpoint.TTL(1), RecordTTL: endpoint.TTL(1),
RecordType: endpoint.RecordTypeCNAME, RecordType: endpoint.RecordTypeCNAME,
}, },
@ -117,13 +117,13 @@ func TestDynMerge_AlwaysUpdateTarget(t *testing.T) {
updateNew := []*endpoint.Endpoint{ updateNew := []*endpoint.Endpoint{
{ {
DNSName: "name1", DNSName: "name1",
Target: "target1-changed", Targets: endpoint.Targets{"target1-changed"},
RecordTTL: endpoint.TTL(0), RecordTTL: endpoint.TTL(0),
RecordType: endpoint.RecordTypeCNAME, RecordType: endpoint.RecordTypeCNAME,
}, },
{ {
DNSName: "name2", DNSName: "name2",
Target: "target2", Targets: endpoint.Targets{"target2"},
RecordTTL: endpoint.TTL(0), RecordTTL: endpoint.TTL(0),
RecordType: endpoint.RecordTypeCNAME, RecordType: endpoint.RecordTypeCNAME,
}, },
@ -131,20 +131,20 @@ func TestDynMerge_AlwaysUpdateTarget(t *testing.T) {
merged := merge(updateOld, updateNew) merged := merge(updateOld, updateNew)
assert.Equal(t, 1, len(merged)) assert.Equal(t, 1, len(merged))
assert.Equal(t, "target1-changed", merged[0].Target) assert.Equal(t, "target1-changed", merged[0].Targets[0])
} }
func TestDynMerge_NoUpdateIfTTLUnchanged(t *testing.T) { func TestDynMerge_NoUpdateIfTTLUnchanged(t *testing.T) {
updateOld := []*endpoint.Endpoint{ updateOld := []*endpoint.Endpoint{
{ {
DNSName: "name1", DNSName: "name1",
Target: "target1", Targets: endpoint.Targets{"target1"},
RecordTTL: endpoint.TTL(55), RecordTTL: endpoint.TTL(55),
RecordType: endpoint.RecordTypeCNAME, RecordType: endpoint.RecordTypeCNAME,
}, },
{ {
DNSName: "name2", DNSName: "name2",
Target: "target2", Targets: endpoint.Targets{"target2"},
RecordTTL: endpoint.TTL(55), RecordTTL: endpoint.TTL(55),
RecordType: endpoint.RecordTypeCNAME, RecordType: endpoint.RecordTypeCNAME,
}, },
@ -153,13 +153,13 @@ func TestDynMerge_NoUpdateIfTTLUnchanged(t *testing.T) {
updateNew := []*endpoint.Endpoint{ updateNew := []*endpoint.Endpoint{
{ {
DNSName: "name1", DNSName: "name1",
Target: "target1", Targets: endpoint.Targets{"target1"},
RecordTTL: endpoint.TTL(55), RecordTTL: endpoint.TTL(55),
RecordType: endpoint.RecordTypeCNAME, RecordType: endpoint.RecordTypeCNAME,
}, },
{ {
DNSName: "name2", DNSName: "name2",
Target: "target2", Targets: endpoint.Targets{"target2"},
RecordTTL: endpoint.TTL(55), RecordTTL: endpoint.TTL(55),
RecordType: endpoint.RecordTypeCNAME, RecordType: endpoint.RecordTypeCNAME,
}, },
@ -271,7 +271,7 @@ func TestDyn_cachePut(t *testing.T) {
c.Put("link", &endpoint.Endpoint{ c.Put("link", &endpoint.Endpoint{
DNSName: "name", DNSName: "name",
Target: "target", Targets: endpoint.Targets{"target"},
RecordTTL: endpoint.TTL(10000), RecordTTL: endpoint.TTL(10000),
RecordType: "A", RecordType: "A",
}) })
@ -287,7 +287,7 @@ func TestDyn_cachePutExpired(t *testing.T) {
c.Put("link", &endpoint.Endpoint{ c.Put("link", &endpoint.Endpoint{
DNSName: "name", DNSName: "name",
Target: "target", Targets: endpoint.Targets{"target"},
RecordTTL: endpoint.TTL(0), RecordTTL: endpoint.TTL(0),
RecordType: "A", RecordType: "A",
}) })

View File

@ -18,6 +18,7 @@ package provider
import ( import (
"fmt" "fmt"
"sort"
"strings" "strings"
"github.com/linki/instrumented_http" "github.com/linki/instrumented_http"
@ -191,13 +192,20 @@ func (p *GoogleProvider) Records() (endpoints []*endpoint.Endpoint, _ error) {
f := func(resp *dns.ResourceRecordSetsListResponse) error { f := func(resp *dns.ResourceRecordSetsListResponse) error {
for _, r := range resp.Rrsets { for _, r := range resp.Rrsets {
if !supportedRecordType(r.Type) {
continue
}
ep := &endpoint.Endpoint{
DNSName: strings.TrimSuffix(r.Name, "."),
RecordType: r.Type,
Targets: make(endpoint.Targets, 0, len(r.Rrdatas)),
}
for _, rr := range r.Rrdatas { for _, rr := range r.Rrdatas {
// each page is processed sequentially, no need for a mutex here. // each page is processed sequentially, no need for a mutex here.
if supportedRecordType(r.Type) { ep.Targets = append(ep.Targets, strings.TrimSuffix(rr, "."))
endpoints = append(endpoints, endpoint.NewEndpoint(r.Name, rr, r.Type))
}
} }
sort.Sort(ep.Targets)
endpoints = append(endpoints, ep)
} }
return nil return nil
@ -347,9 +355,10 @@ func newRecord(ep *endpoint.Endpoint) *dns.ResourceRecordSet {
// TODO(linki): works around appending a trailing dot to TXT records. I think // TODO(linki): works around appending a trailing dot to TXT records. I think
// we should go back to storing DNS names with a trailing dot internally. This // we should go back to storing DNS names with a trailing dot internally. This
// way we can use it has is here and trim it off if it exists when necessary. // way we can use it has is here and trim it off if it exists when necessary.
target := ep.Target targets := make([]string, len(ep.Targets))
copy(targets, []string(ep.Targets))
if ep.RecordType == endpoint.RecordTypeCNAME { if ep.RecordType == endpoint.RecordTypeCNAME {
target = ensureTrailingDot(target) targets[0] = ensureTrailingDot(targets[0])
} }
// no annotation results in a Ttl of 0, default to 300 for backwards-compatability // no annotation results in a Ttl of 0, default to 300 for backwards-compatability
@ -360,7 +369,7 @@ func newRecord(ep *endpoint.Endpoint) *dns.ResourceRecordSet {
return &dns.ResourceRecordSet{ return &dns.ResourceRecordSet{
Name: ensureTrailingDot(ep.DNSName), Name: ensureTrailingDot(ep.DNSName),
Rrdatas: []string{target}, Rrdatas: targets,
Ttl: ttl, Ttl: ttl,
Type: ep.RecordType, Type: ep.RecordType,
} }

View File

@ -257,7 +257,7 @@ func (p *InfobloxProvider) recordSet(ep *endpoint.Endpoint, getObject bool) (rec
obj := ibclient.NewRecordA( obj := ibclient.NewRecordA(
ibclient.RecordA{ ibclient.RecordA{
Name: ep.DNSName, Name: ep.DNSName,
Ipv4Addr: ep.Target, Ipv4Addr: ep.Targets[0],
}, },
) )
if getObject { if getObject {
@ -275,7 +275,7 @@ func (p *InfobloxProvider) recordSet(ep *endpoint.Endpoint, getObject bool) (rec
obj := ibclient.NewRecordCNAME( obj := ibclient.NewRecordCNAME(
ibclient.RecordCNAME{ ibclient.RecordCNAME{
Name: ep.DNSName, Name: ep.DNSName,
Canonical: ep.Target, Canonical: ep.Targets[0],
}, },
) )
if getObject { if getObject {
@ -292,13 +292,13 @@ func (p *InfobloxProvider) recordSet(ep *endpoint.Endpoint, getObject bool) (rec
var res []ibclient.RecordTXT var res []ibclient.RecordTXT
// The Infoblox API strips enclosing double quotes from TXT records lacking whitespace. // The Infoblox API strips enclosing double quotes from TXT records lacking whitespace.
// Here we reconcile that fact by making this state match that reality. // Here we reconcile that fact by making this state match that reality.
if target, err2 := strconv.Unquote(ep.Target); err2 == nil && !strings.Contains(ep.Target, " ") { if target, err2 := strconv.Unquote(ep.Targets[0]); err2 == nil && !strings.Contains(ep.Targets[0], " ") {
ep.Target = target ep.Targets = endpoint.Targets{target}
} }
obj := ibclient.NewRecordTXT( obj := ibclient.NewRecordTXT(
ibclient.RecordTXT{ ibclient.RecordTXT{
Name: ep.DNSName, Name: ep.DNSName,
Text: ep.Target, Text: ep.Targets[0],
}, },
) )
if getObject { if getObject {
@ -323,7 +323,7 @@ func (p *InfobloxProvider) createRecords(created infobloxChangeMap) {
"Would create %s record named '%s' to '%s' for Infoblox DNS zone '%s'.", "Would create %s record named '%s' to '%s' for Infoblox DNS zone '%s'.",
ep.RecordType, ep.RecordType,
ep.DNSName, ep.DNSName,
ep.Target, ep.Targets,
zone, zone,
) )
continue continue
@ -333,7 +333,7 @@ func (p *InfobloxProvider) createRecords(created infobloxChangeMap) {
"Creating %s record named '%s' to '%s' for Infoblox DNS zone '%s'.", "Creating %s record named '%s' to '%s' for Infoblox DNS zone '%s'.",
ep.RecordType, ep.RecordType,
ep.DNSName, ep.DNSName,
ep.Target, ep.Targets,
zone, zone,
) )
@ -343,7 +343,7 @@ func (p *InfobloxProvider) createRecords(created infobloxChangeMap) {
"Failed to retrieve %s record named '%s' to '%s' for DNS zone '%s': %v", "Failed to retrieve %s record named '%s' to '%s' for DNS zone '%s': %v",
ep.RecordType, ep.RecordType,
ep.DNSName, ep.DNSName,
ep.Target, ep.Targets,
zone, zone,
err, err,
) )
@ -355,7 +355,7 @@ func (p *InfobloxProvider) createRecords(created infobloxChangeMap) {
"Failed to create %s record named '%s' to '%s' for DNS zone '%s': %v", "Failed to create %s record named '%s' to '%s' for DNS zone '%s': %v",
ep.RecordType, ep.RecordType,
ep.DNSName, ep.DNSName,
ep.Target, ep.Targets,
zone, zone,
err, err,
) )
@ -378,7 +378,7 @@ func (p *InfobloxProvider) deleteRecords(deleted infobloxChangeMap) {
"Failed to retrieve %s record named '%s' to '%s' for DNS zone '%s': %v", "Failed to retrieve %s record named '%s' to '%s' for DNS zone '%s': %v",
ep.RecordType, ep.RecordType,
ep.DNSName, ep.DNSName,
ep.Target, ep.Targets,
zone, zone,
err, err,
) )

View File

@ -203,7 +203,7 @@ func convertToInMemoryRecord(endpoints []*endpoint.Endpoint) []*inMemoryRecord {
records = append(records, &inMemoryRecord{ records = append(records, &inMemoryRecord{
Type: ep.RecordType, Type: ep.RecordType,
Name: ep.DNSName, Name: ep.DNSName,
Target: ep.Target, Target: ep.Targets[0],
}) })
} }
return records return records

View File

@ -186,16 +186,17 @@ func testInMemoryRecords(t *testing.T) {
expected: []*endpoint.Endpoint{ expected: []*endpoint.Endpoint{
{ {
DNSName: "example.org", DNSName: "example.org",
Target: "8.8.8.8", Targets: endpoint.Targets{"8.8.8.8"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
}, },
{ {
DNSName: "example.org", DNSName: "example.org",
RecordType: endpoint.RecordTypeTXT, RecordType: endpoint.RecordTypeTXT,
Targets: endpoint.Targets{""},
}, },
{ {
DNSName: "foo.org", DNSName: "foo.org",
Target: "4.4.4.4", Targets: endpoint.Targets{"4.4.4.4"},
RecordType: endpoint.RecordTypeCNAME, RecordType: endpoint.RecordTypeCNAME,
}, },
}, },
@ -214,7 +215,7 @@ func testInMemoryRecords(t *testing.T) {
assert.EqualError(t, err, ErrZoneNotFound.Error()) assert.EqualError(t, err, ErrZoneNotFound.Error())
} else { } else {
require.NoError(t, err) require.NoError(t, err)
assert.True(t, testutils.SameEndpoints(ti.expected, records)) assert.True(t, testutils.SameEndpoints(ti.expected, records), "Endpoints not the same: Expected: %+v Records: %+v", ti.expected, records)
} }
}) })
} }
@ -314,7 +315,7 @@ func testInMemoryValidateChangeBatch(t *testing.T) {
Create: []*endpoint.Endpoint{ Create: []*endpoint.Endpoint{
{ {
DNSName: "example.org", DNSName: "example.org",
Target: "8.8.8.8", Targets: endpoint.Targets{"8.8.8.8"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
}, },
}, },
@ -333,14 +334,14 @@ func testInMemoryValidateChangeBatch(t *testing.T) {
Create: []*endpoint.Endpoint{ Create: []*endpoint.Endpoint{
{ {
DNSName: "foo.org", DNSName: "foo.org",
Target: "4.4.4.4", Targets: endpoint.Targets{"4.4.4.4"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
}, },
}, },
UpdateNew: []*endpoint.Endpoint{ UpdateNew: []*endpoint.Endpoint{
{ {
DNSName: "foo.org", DNSName: "foo.org",
Target: "4.4.4.4", Targets: endpoint.Targets{"4.4.4.4"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
}, },
}, },
@ -358,14 +359,14 @@ func testInMemoryValidateChangeBatch(t *testing.T) {
Create: []*endpoint.Endpoint{ Create: []*endpoint.Endpoint{
{ {
DNSName: "foo.org", DNSName: "foo.org",
Target: "4.4.4.4", Targets: endpoint.Targets{"4.4.4.4"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
}, },
}, },
UpdateNew: []*endpoint.Endpoint{ UpdateNew: []*endpoint.Endpoint{
{ {
DNSName: "foo.org", DNSName: "foo.org",
Target: "4.4.4.4", Targets: endpoint.Targets{"4.4.4.4"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
}, },
}, },
@ -383,12 +384,12 @@ func testInMemoryValidateChangeBatch(t *testing.T) {
Create: []*endpoint.Endpoint{ Create: []*endpoint.Endpoint{
{ {
DNSName: "foo.org", DNSName: "foo.org",
Target: "4.4.4.4", Targets: endpoint.Targets{"4.4.4.4"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
}, },
{ {
DNSName: "foo.org", DNSName: "foo.org",
Target: "4.4.4.4", Targets: endpoint.Targets{"4.4.4.4"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
}, },
}, },
@ -408,7 +409,7 @@ func testInMemoryValidateChangeBatch(t *testing.T) {
UpdateNew: []*endpoint.Endpoint{ UpdateNew: []*endpoint.Endpoint{
{ {
DNSName: "example.org", DNSName: "example.org",
Target: "8.8.8.8", Targets: endpoint.Targets{"8.8.8.8"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
}, },
}, },
@ -416,7 +417,7 @@ func testInMemoryValidateChangeBatch(t *testing.T) {
Delete: []*endpoint.Endpoint{ Delete: []*endpoint.Endpoint{
{ {
DNSName: "example.org", DNSName: "example.org",
Target: "8.8.8.8", Targets: endpoint.Targets{"8.8.8.8"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
}, },
}, },
@ -433,12 +434,12 @@ func testInMemoryValidateChangeBatch(t *testing.T) {
UpdateNew: []*endpoint.Endpoint{ UpdateNew: []*endpoint.Endpoint{
{ {
DNSName: "example.org", DNSName: "example.org",
Target: "8.8.8.8", Targets: endpoint.Targets{"8.8.8.8"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
}, },
{ {
DNSName: "example.org", DNSName: "example.org",
Target: "8.8.8.8", Targets: endpoint.Targets{"8.8.8.8"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
}, },
}, },
@ -458,7 +459,7 @@ func testInMemoryValidateChangeBatch(t *testing.T) {
UpdateOld: []*endpoint.Endpoint{ UpdateOld: []*endpoint.Endpoint{
{ {
DNSName: "new.org", DNSName: "new.org",
Target: "8.8.8.8", Targets: endpoint.Targets{"8.8.8.8"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
}, },
}, },
@ -478,7 +479,7 @@ func testInMemoryValidateChangeBatch(t *testing.T) {
Delete: []*endpoint.Endpoint{ Delete: []*endpoint.Endpoint{
{ {
DNSName: "new.org", DNSName: "new.org",
Target: "8.8.8.8", Targets: endpoint.Targets{"8.8.8.8"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
}, },
}, },
@ -497,7 +498,7 @@ func testInMemoryValidateChangeBatch(t *testing.T) {
Delete: []*endpoint.Endpoint{ Delete: []*endpoint.Endpoint{
{ {
DNSName: "foo.bar.org", DNSName: "foo.bar.org",
Target: "5.5.5.5", Targets: endpoint.Targets{"5.5.5.5"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
}, },
}, },
@ -512,21 +513,21 @@ func testInMemoryValidateChangeBatch(t *testing.T) {
Create: []*endpoint.Endpoint{ Create: []*endpoint.Endpoint{
{ {
DNSName: "foo.bar.new.org", DNSName: "foo.bar.new.org",
Target: "4.8.8.9", Targets: endpoint.Targets{"4.8.8.9"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
}, },
}, },
UpdateNew: []*endpoint.Endpoint{ UpdateNew: []*endpoint.Endpoint{
{ {
DNSName: "foo.bar.org", DNSName: "foo.bar.org",
Target: "4.8.8.4", Targets: endpoint.Targets{"4.8.8.4"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
}, },
}, },
UpdateOld: []*endpoint.Endpoint{ UpdateOld: []*endpoint.Endpoint{
{ {
DNSName: "foo.bar.org", DNSName: "foo.bar.org",
Target: "5.5.5.5", Targets: endpoint.Targets{"5.5.5.5"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
}, },
}, },
@ -608,7 +609,7 @@ func testInMemoryApplyChanges(t *testing.T) {
changes: &plan.Changes{ changes: &plan.Changes{
Create: []*endpoint.Endpoint{{ Create: []*endpoint.Endpoint{{
DNSName: "example.de", DNSName: "example.de",
Target: "8.8.8.8", Targets: endpoint.Targets{"8.8.8.8"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
}}, }},
UpdateNew: []*endpoint.Endpoint{}, UpdateNew: []*endpoint.Endpoint{},
@ -625,7 +626,7 @@ func testInMemoryApplyChanges(t *testing.T) {
UpdateNew: []*endpoint.Endpoint{ UpdateNew: []*endpoint.Endpoint{
{ {
DNSName: "example.org", DNSName: "example.org",
Target: "8.8.8.8", Targets: endpoint.Targets{"8.8.8.8"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
}, },
}, },
@ -633,7 +634,7 @@ func testInMemoryApplyChanges(t *testing.T) {
Delete: []*endpoint.Endpoint{ Delete: []*endpoint.Endpoint{
{ {
DNSName: "example.org", DNSName: "example.org",
Target: "8.8.8.8", Targets: endpoint.Targets{"8.8.8.8"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
}, },
}, },
@ -649,7 +650,7 @@ func testInMemoryApplyChanges(t *testing.T) {
Delete: []*endpoint.Endpoint{ Delete: []*endpoint.Endpoint{
{ {
DNSName: "foo.bar.org", DNSName: "foo.bar.org",
Target: "5.5.5.5", Targets: endpoint.Targets{"5.5.5.5"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
}, },
}, },
@ -697,28 +698,28 @@ func testInMemoryApplyChanges(t *testing.T) {
Create: []*endpoint.Endpoint{ Create: []*endpoint.Endpoint{
{ {
DNSName: "foo.bar.new.org", DNSName: "foo.bar.new.org",
Target: "4.8.8.9", Targets: endpoint.Targets{"4.8.8.9"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
}, },
}, },
UpdateNew: []*endpoint.Endpoint{ UpdateNew: []*endpoint.Endpoint{
{ {
DNSName: "foo.bar.org", DNSName: "foo.bar.org",
Target: "4.8.8.4", Targets: endpoint.Targets{"4.8.8.4"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
}, },
}, },
UpdateOld: []*endpoint.Endpoint{ UpdateOld: []*endpoint.Endpoint{
{ {
DNSName: "foo.bar.org", DNSName: "foo.bar.org",
Target: "5.5.5.5", Targets: endpoint.Targets{"5.5.5.5"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
}, },
}, },
Delete: []*endpoint.Endpoint{ Delete: []*endpoint.Endpoint{
{ {
DNSName: "example.org", DNSName: "example.org",
Target: "8.8.8.8", Targets: endpoint.Targets{"8.8.8.8"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
}, },
}, },

View File

@ -49,7 +49,7 @@ func testNoopRecords(t *testing.T) {
providerRecords := []*endpoint.Endpoint{ providerRecords := []*endpoint.Endpoint{
{ {
DNSName: "example.org", DNSName: "example.org",
Target: "example-lb.com", Targets: endpoint.Targets{"example-lb.com"},
RecordType: endpoint.RecordTypeCNAME, RecordType: endpoint.RecordTypeCNAME,
}, },
} }
@ -71,19 +71,19 @@ func testNoopApplyChanges(t *testing.T) {
providerRecords := []*endpoint.Endpoint{ providerRecords := []*endpoint.Endpoint{
{ {
DNSName: "example.org", DNSName: "example.org",
Target: "old-lb.com", Targets: endpoint.Targets{"old-lb.com"},
RecordType: endpoint.RecordTypeCNAME, RecordType: endpoint.RecordTypeCNAME,
}, },
} }
expectedUpdate := []*endpoint.Endpoint{ expectedUpdate := []*endpoint.Endpoint{
{ {
DNSName: "example.org", DNSName: "example.org",
Target: "new-example-lb.com", Targets: endpoint.Targets{"new-example-lb.com"},
RecordType: endpoint.RecordTypeCNAME, RecordType: endpoint.RecordTypeCNAME,
}, },
{ {
DNSName: "new-record.org", DNSName: "new-record.org",
Target: "new-lb.org", Targets: endpoint.Targets{"new-lb.org"},
RecordType: endpoint.RecordTypeCNAME, RecordType: endpoint.RecordTypeCNAME,
}, },
} }
@ -98,7 +98,7 @@ func testNoopApplyChanges(t *testing.T) {
Create: []*endpoint.Endpoint{ Create: []*endpoint.Endpoint{
{ {
DNSName: "example.org", DNSName: "example.org",
Target: "lb.com", Targets: endpoint.Targets{"lb.com"},
RecordType: endpoint.RecordTypeCNAME, RecordType: endpoint.RecordTypeCNAME,
}, },
}, },
@ -110,21 +110,21 @@ func testNoopApplyChanges(t *testing.T) {
Create: []*endpoint.Endpoint{ Create: []*endpoint.Endpoint{
{ {
DNSName: "new-record.org", DNSName: "new-record.org",
Target: "new-lb.org", Targets: endpoint.Targets{"new-lb.org"},
RecordType: endpoint.RecordTypeCNAME, RecordType: endpoint.RecordTypeCNAME,
}, },
}, },
UpdateNew: []*endpoint.Endpoint{ UpdateNew: []*endpoint.Endpoint{
{ {
DNSName: "example.org", DNSName: "example.org",
Target: "new-example-lb.com", Targets: endpoint.Targets{"new-example-lb.com"},
RecordType: endpoint.RecordTypeCNAME, RecordType: endpoint.RecordTypeCNAME,
}, },
}, },
UpdateOld: []*endpoint.Endpoint{ UpdateOld: []*endpoint.Endpoint{
{ {
DNSName: "example.org", DNSName: "example.org",
Target: "old-lb.com", Targets: endpoint.Targets{"old-lb.com"},
RecordType: endpoint.RecordTypeCNAME, RecordType: endpoint.RecordTypeCNAME,
}, },
}, },

View File

@ -66,7 +66,8 @@ func (im *TXTRegistry) Records() ([]*endpoint.Endpoint, error) {
endpoints = append(endpoints, record) endpoints = append(endpoints, record)
continue continue
} }
labels, err := endpoint.NewLabelsFromString(record.Target) // We simply assume that TXT records for the registry will always have only one target.
labels, err := endpoint.NewLabelsFromString(record.Targets[0])
if err == endpoint.ErrInvalidHeritage { if err == endpoint.ErrInvalidHeritage {
//if no heritage is found or it is invalid //if no heritage is found or it is invalid
//case when value of txt record cannot be identified //case when value of txt record cannot be identified

View File

@ -82,7 +82,7 @@ func testTXTRegistryRecordsPrefixed(t *testing.T) {
expectedRecords := []*endpoint.Endpoint{ expectedRecords := []*endpoint.Endpoint{
{ {
DNSName: "foo.test-zone.example.org", DNSName: "foo.test-zone.example.org",
Target: "foo.loadbalancer.com", Targets: endpoint.Targets{"foo.loadbalancer.com"},
RecordType: endpoint.RecordTypeCNAME, RecordType: endpoint.RecordTypeCNAME,
Labels: map[string]string{ Labels: map[string]string{
endpoint.OwnerLabelKey: "", endpoint.OwnerLabelKey: "",
@ -90,7 +90,7 @@ func testTXTRegistryRecordsPrefixed(t *testing.T) {
}, },
{ {
DNSName: "bar.test-zone.example.org", DNSName: "bar.test-zone.example.org",
Target: "my-domain.com", Targets: endpoint.Targets{"my-domain.com"},
RecordType: endpoint.RecordTypeCNAME, RecordType: endpoint.RecordTypeCNAME,
Labels: map[string]string{ Labels: map[string]string{
endpoint.OwnerLabelKey: "owner", endpoint.OwnerLabelKey: "owner",
@ -98,7 +98,7 @@ func testTXTRegistryRecordsPrefixed(t *testing.T) {
}, },
{ {
DNSName: "txt.bar.test-zone.example.org", DNSName: "txt.bar.test-zone.example.org",
Target: "baz.test-zone.example.org", Targets: endpoint.Targets{"baz.test-zone.example.org"},
RecordType: endpoint.RecordTypeCNAME, RecordType: endpoint.RecordTypeCNAME,
Labels: map[string]string{ Labels: map[string]string{
endpoint.OwnerLabelKey: "", endpoint.OwnerLabelKey: "",
@ -106,7 +106,7 @@ func testTXTRegistryRecordsPrefixed(t *testing.T) {
}, },
{ {
DNSName: "qux.test-zone.example.org", DNSName: "qux.test-zone.example.org",
Target: "random", Targets: endpoint.Targets{"random"},
RecordType: endpoint.RecordTypeTXT, RecordType: endpoint.RecordTypeTXT,
Labels: map[string]string{ Labels: map[string]string{
endpoint.OwnerLabelKey: "", endpoint.OwnerLabelKey: "",
@ -114,7 +114,7 @@ func testTXTRegistryRecordsPrefixed(t *testing.T) {
}, },
{ {
DNSName: "tar.test-zone.example.org", DNSName: "tar.test-zone.example.org",
Target: "tar.loadbalancer.com", Targets: endpoint.Targets{"tar.loadbalancer.com"},
RecordType: endpoint.RecordTypeCNAME, RecordType: endpoint.RecordTypeCNAME,
Labels: map[string]string{ Labels: map[string]string{
endpoint.OwnerLabelKey: "owner-2", endpoint.OwnerLabelKey: "owner-2",
@ -122,7 +122,7 @@ func testTXTRegistryRecordsPrefixed(t *testing.T) {
}, },
{ {
DNSName: "foobar.test-zone.example.org", DNSName: "foobar.test-zone.example.org",
Target: "foobar.loadbalancer.com", Targets: endpoint.Targets{"foobar.loadbalancer.com"},
RecordType: endpoint.RecordTypeCNAME, RecordType: endpoint.RecordTypeCNAME,
Labels: map[string]string{ Labels: map[string]string{
endpoint.OwnerLabelKey: "", endpoint.OwnerLabelKey: "",
@ -155,7 +155,7 @@ func testTXTRegistryRecordsNoPrefix(t *testing.T) {
expectedRecords := []*endpoint.Endpoint{ expectedRecords := []*endpoint.Endpoint{
{ {
DNSName: "foo.test-zone.example.org", DNSName: "foo.test-zone.example.org",
Target: "foo.loadbalancer.com", Targets: endpoint.Targets{"foo.loadbalancer.com"},
RecordType: endpoint.RecordTypeCNAME, RecordType: endpoint.RecordTypeCNAME,
Labels: map[string]string{ Labels: map[string]string{
endpoint.OwnerLabelKey: "", endpoint.OwnerLabelKey: "",
@ -163,7 +163,7 @@ func testTXTRegistryRecordsNoPrefix(t *testing.T) {
}, },
{ {
DNSName: "bar.test-zone.example.org", DNSName: "bar.test-zone.example.org",
Target: "my-domain.com", Targets: endpoint.Targets{"my-domain.com"},
RecordType: endpoint.RecordTypeCNAME, RecordType: endpoint.RecordTypeCNAME,
Labels: map[string]string{ Labels: map[string]string{
endpoint.OwnerLabelKey: "", endpoint.OwnerLabelKey: "",
@ -171,7 +171,7 @@ func testTXTRegistryRecordsNoPrefix(t *testing.T) {
}, },
{ {
DNSName: "txt.bar.test-zone.example.org", DNSName: "txt.bar.test-zone.example.org",
Target: "baz.test-zone.example.org", Targets: endpoint.Targets{"baz.test-zone.example.org"},
RecordType: endpoint.RecordTypeCNAME, RecordType: endpoint.RecordTypeCNAME,
Labels: map[string]string{ Labels: map[string]string{
endpoint.OwnerLabelKey: "owner", endpoint.OwnerLabelKey: "owner",
@ -180,7 +180,7 @@ func testTXTRegistryRecordsNoPrefix(t *testing.T) {
}, },
{ {
DNSName: "qux.test-zone.example.org", DNSName: "qux.test-zone.example.org",
Target: "random", Targets: endpoint.Targets{"random"},
RecordType: endpoint.RecordTypeTXT, RecordType: endpoint.RecordTypeTXT,
Labels: map[string]string{ Labels: map[string]string{
endpoint.OwnerLabelKey: "", endpoint.OwnerLabelKey: "",
@ -188,7 +188,7 @@ func testTXTRegistryRecordsNoPrefix(t *testing.T) {
}, },
{ {
DNSName: "tar.test-zone.example.org", DNSName: "tar.test-zone.example.org",
Target: "tar.loadbalancer.com", Targets: endpoint.Targets{"tar.loadbalancer.com"},
RecordType: endpoint.RecordTypeCNAME, RecordType: endpoint.RecordTypeCNAME,
Labels: map[string]string{ Labels: map[string]string{
endpoint.OwnerLabelKey: "", endpoint.OwnerLabelKey: "",
@ -196,7 +196,7 @@ func testTXTRegistryRecordsNoPrefix(t *testing.T) {
}, },
{ {
DNSName: "foobar.test-zone.example.org", DNSName: "foobar.test-zone.example.org",
Target: "foobar.loadbalancer.com", Targets: endpoint.Targets{"foobar.loadbalancer.com"},
RecordType: endpoint.RecordTypeCNAME, RecordType: endpoint.RecordTypeCNAME,
Labels: map[string]string{ Labels: map[string]string{
endpoint.OwnerLabelKey: "owner", endpoint.OwnerLabelKey: "owner",

View File

@ -43,7 +43,7 @@ func (ms *dedupSource) Endpoints() ([]*endpoint.Endpoint, error) {
} }
for _, ep := range endpoints { for _, ep := range endpoints {
identifier := ep.DNSName + " / " + ep.Target identifier := ep.DNSName + " / " + ep.Targets.String()
if _, ok := collected[identifier]; ok { if _, ok := collected[identifier]; ok {
log.Debugf("Removing duplicate endpoint %s", ep) log.Debugf("Removing duplicate endpoint %s", ep)

View File

@ -40,53 +40,53 @@ func testDedupEndpoints(t *testing.T) {
{ {
"one endpoint returns one endpoint", "one endpoint returns one endpoint",
[]*endpoint.Endpoint{ []*endpoint.Endpoint{
{DNSName: "foo.example.org", Target: "1.2.3.4"}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
}, },
[]*endpoint.Endpoint{ []*endpoint.Endpoint{
{DNSName: "foo.example.org", Target: "1.2.3.4"}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
}, },
}, },
{ {
"two different endpoints return two endpoints", "two different endpoints return two endpoints",
[]*endpoint.Endpoint{ []*endpoint.Endpoint{
{DNSName: "foo.example.org", Target: "1.2.3.4"}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
{DNSName: "bar.example.org", Target: "4.5.6.7"}, {DNSName: "bar.example.org", Targets: endpoint.Targets{"4.5.6.7"}},
}, },
[]*endpoint.Endpoint{ []*endpoint.Endpoint{
{DNSName: "foo.example.org", Target: "1.2.3.4"}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
{DNSName: "bar.example.org", Target: "4.5.6.7"}, {DNSName: "bar.example.org", Targets: endpoint.Targets{"4.5.6.7"}},
}, },
}, },
{ {
"two endpoints with same dnsname and different targets return two endpoints", "two endpoints with same dnsname and different targets return two endpoints",
[]*endpoint.Endpoint{ []*endpoint.Endpoint{
{DNSName: "foo.example.org", Target: "1.2.3.4"}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
{DNSName: "foo.example.org", Target: "4.5.6.7"}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"4.5.6.7"}},
}, },
[]*endpoint.Endpoint{ []*endpoint.Endpoint{
{DNSName: "foo.example.org", Target: "1.2.3.4"}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
{DNSName: "foo.example.org", Target: "4.5.6.7"}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"4.5.6.7"}},
}, },
}, },
{ {
"two endpoints with different dnsname and same target return two endpoints", "two endpoints with different dnsname and same target return two endpoints",
[]*endpoint.Endpoint{ []*endpoint.Endpoint{
{DNSName: "foo.example.org", Target: "1.2.3.4"}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
{DNSName: "bar.example.org", Target: "1.2.3.4"}, {DNSName: "bar.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
}, },
[]*endpoint.Endpoint{ []*endpoint.Endpoint{
{DNSName: "foo.example.org", Target: "1.2.3.4"}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
{DNSName: "bar.example.org", Target: "1.2.3.4"}, {DNSName: "bar.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
}, },
}, },
{ {
"two endpoints with same dnsname and same target return one endpoint", "two endpoints with same dnsname and same target return one endpoint",
[]*endpoint.Endpoint{ []*endpoint.Endpoint{
{DNSName: "foo.example.org", Target: "1.2.3.4"}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
{DNSName: "foo.example.org", Target: "1.2.3.4"}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
}, },
[]*endpoint.Endpoint{ []*endpoint.Endpoint{
{DNSName: "foo.example.org", Target: "1.2.3.4"}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
}, },
}, },
} { } {

View File

@ -60,7 +60,7 @@ func TestFakeEndpointsResolveToIPAddresses(t *testing.T) {
endpoints := generateTestEndpoints() endpoints := generateTestEndpoints()
for _, e := range endpoints { for _, e := range endpoints {
ip := net.ParseIP(e.Target) ip := net.ParseIP(e.Targets[0])
if ip == nil { if ip == nil {
t.Error(e) t.Error(e)

View File

@ -19,6 +19,7 @@ package source
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"sort"
"strings" "strings"
"text/template" "text/template"
@ -109,33 +110,32 @@ func (sc *ingressSource) Endpoints() ([]*endpoint.Endpoint, error) {
endpoints = append(endpoints, ingEndpoints...) endpoints = append(endpoints, ingEndpoints...)
} }
for _, ep := range endpoints {
sort.Sort(ep.Targets)
}
return endpoints, nil return endpoints, nil
} }
// get endpoints from optional "target" annotation // get endpoints from optional "target" annotation
// Returns empty endpoints array if none are found. // Returns empty endpoints array if none are found.
func getEndpointsFromTargetAnnotation(ing *v1beta1.Ingress, hostname string) []*endpoint.Endpoint { func getTargetsFromTargetAnnotation(ing *v1beta1.Ingress) endpoint.Targets {
var endpoints []*endpoint.Endpoint var targets endpoint.Targets
// Get the desired hostname of the ingress from the annotation. // Get the desired hostname of the ingress from the annotation.
targetAnnotation, exists := ing.Annotations[targetAnnotationKey] targetAnnotation, exists := ing.Annotations[targetAnnotationKey]
if exists { if exists {
ttl, err := getTTLFromAnnotations(ing.Annotations)
if err != nil {
log.Warn(err)
}
// splits the hostname annotation and removes the trailing periods // splits the hostname annotation and removes the trailing periods
targetsList := strings.Split(strings.Replace(targetAnnotation, " ", "", -1), ",") targetsList := strings.Split(strings.Replace(targetAnnotation, " ", "", -1), ",")
for _, targetHostname := range targetsList { for _, targetHostname := range targetsList {
targetHostname = strings.TrimSuffix(targetHostname, ".") targetHostname = strings.TrimSuffix(targetHostname, ".")
endpoints = append(endpoints, endpoint.NewEndpointWithTTL(hostname, targetHostname, suitableType(targetHostname), ttl)) targets = append(targets, targetHostname)
} }
} }
return endpoints return targets
} }
func (sc *ingressSource) endpointsFromTemplate(ing *v1beta1.Ingress) ([]*endpoint.Endpoint, error) { func (sc *ingressSource) endpointsFromTemplate(ing *v1beta1.Ingress) ([]*endpoint.Endpoint, error) {
var endpoints []*endpoint.Endpoint
var buf bytes.Buffer var buf bytes.Buffer
err := sc.fqdnTemplate.Execute(&buf, ing) err := sc.fqdnTemplate.Execute(&buf, ing)
@ -145,26 +145,18 @@ func (sc *ingressSource) endpointsFromTemplate(ing *v1beta1.Ingress) ([]*endpoin
hostname := buf.String() hostname := buf.String()
endpoints = getEndpointsFromTargetAnnotation(ing, hostname)
if len(endpoints) != 0 {
return endpoints, nil
}
ttl, err := getTTLFromAnnotations(ing.Annotations) ttl, err := getTTLFromAnnotations(ing.Annotations)
if err != nil { if err != nil {
log.Warn(err) log.Warn(err)
} }
for _, lb := range ing.Status.LoadBalancer.Ingress {
if lb.IP != "" { targets := getTargetsFromTargetAnnotation(ing)
endpoints = append(endpoints, endpoint.NewEndpointWithTTL(hostname, lb.IP, endpoint.RecordTypeA, ttl))
} if len(targets) == 0 {
if lb.Hostname != "" { targets = targetsFromIngressStatus(ing.Status)
endpoints = append(endpoints, endpoint.NewEndpointWithTTL(hostname, lb.Hostname, endpoint.RecordTypeCNAME, ttl))
}
} }
return endpoints, nil return endpointsForHostname(hostname, targets, ttl), nil
} }
// filterByAnnotations filters a list of ingresses by a given annotation selector. // filterByAnnotations filters a list of ingresses by a given annotation selector.
@ -208,32 +200,77 @@ func (sc *ingressSource) setResourceLabel(ingress v1beta1.Ingress, endpoints []*
func endpointsFromIngress(ing *v1beta1.Ingress) []*endpoint.Endpoint { func endpointsFromIngress(ing *v1beta1.Ingress) []*endpoint.Endpoint {
var endpoints []*endpoint.Endpoint var endpoints []*endpoint.Endpoint
ttl, err := getTTLFromAnnotations(ing.Annotations)
if err != nil {
log.Warn(err)
}
targets := getTargetsFromTargetAnnotation(ing)
if len(targets) == 0 {
targets = targetsFromIngressStatus(ing.Status)
}
for _, rule := range ing.Spec.Rules { for _, rule := range ing.Spec.Rules {
if rule.Host == "" { if rule.Host == "" {
continue continue
} }
endpoints = append(endpoints, endpointsForHostname(rule.Host, targets, ttl)...)
}
return endpoints
}
annotationEndpoints := getEndpointsFromTargetAnnotation(ing, rule.Host) func endpointsForHostname(hostname string, targets endpoint.Targets, ttl endpoint.TTL) []*endpoint.Endpoint {
var endpoints []*endpoint.Endpoint
if len(annotationEndpoints) != 0 { var aTargets endpoint.Targets
endpoints = append(endpoints, annotationEndpoints...) var cnameTargets endpoint.Targets
continue
for _, t := range targets {
switch suitableType(t) {
case endpoint.RecordTypeA:
aTargets = append(aTargets, t)
default:
cnameTargets = append(cnameTargets, t)
} }
}
ttl, err := getTTLFromAnnotations(ing.Annotations) if len(aTargets) > 0 {
if err != nil { epA := &endpoint.Endpoint{
log.Warn(err) DNSName: strings.TrimSuffix(hostname, "."),
Targets: aTargets,
RecordTTL: ttl,
RecordType: endpoint.RecordTypeA,
Labels: endpoint.NewLabels(),
} }
endpoints = append(endpoints, epA)
}
for _, lb := range ing.Status.LoadBalancer.Ingress { if len(cnameTargets) > 0 {
if lb.IP != "" { epCNAME := &endpoint.Endpoint{
endpoints = append(endpoints, endpoint.NewEndpointWithTTL(rule.Host, lb.IP, endpoint.RecordTypeA, ttl)) DNSName: strings.TrimSuffix(hostname, "."),
} Targets: cnameTargets,
if lb.Hostname != "" { RecordTTL: ttl,
endpoints = append(endpoints, endpoint.NewEndpointWithTTL(rule.Host, lb.Hostname, endpoint.RecordTypeCNAME, ttl)) RecordType: endpoint.RecordTypeCNAME,
} Labels: endpoint.NewLabels(),
} }
endpoints = append(endpoints, epCNAME)
} }
return endpoints return endpoints
} }
func targetsFromIngressStatus(status v1beta1.IngressStatus) endpoint.Targets {
var targets endpoint.Targets
for _, lb := range status.LoadBalancer.Ingress {
if lb.IP != "" {
targets = append(targets, lb.IP)
}
if lb.Hostname != "" {
targets = append(targets, lb.Hostname)
}
}
return targets
}

View File

@ -134,7 +134,7 @@ func testEndpointsFromIngress(t *testing.T) {
expected: []*endpoint.Endpoint{ expected: []*endpoint.Endpoint{
{ {
DNSName: "foo.bar", DNSName: "foo.bar",
Target: "lb.com", Targets: endpoint.Targets{"lb.com"},
}, },
}, },
}, },
@ -147,7 +147,7 @@ func testEndpointsFromIngress(t *testing.T) {
expected: []*endpoint.Endpoint{ expected: []*endpoint.Endpoint{
{ {
DNSName: "foo.bar", DNSName: "foo.bar",
Target: "8.8.8.8", Targets: endpoint.Targets{"8.8.8.8"},
}, },
}, },
}, },
@ -161,19 +161,11 @@ func testEndpointsFromIngress(t *testing.T) {
expected: []*endpoint.Endpoint{ expected: []*endpoint.Endpoint{
{ {
DNSName: "foo.bar", DNSName: "foo.bar",
Target: "8.8.8.8", Targets: endpoint.Targets{"8.8.8.8", "127.0.0.1"},
}, },
{ {
DNSName: "foo.bar", DNSName: "foo.bar",
Target: "127.0.0.1", Targets: endpoint.Targets{"elb.com", "alb.com"},
},
{
DNSName: "foo.bar",
Target: "elb.com",
},
{
DNSName: "foo.bar",
Target: "alb.com",
}, },
}, },
}, },
@ -244,11 +236,11 @@ func testIngressEndpoints(t *testing.T) {
expected: []*endpoint.Endpoint{ expected: []*endpoint.Endpoint{
{ {
DNSName: "example.org", DNSName: "example.org",
Target: "8.8.8.8", Targets: endpoint.Targets{"8.8.8.8"},
}, },
{ {
DNSName: "new.org", DNSName: "new.org",
Target: "lb.com", Targets: endpoint.Targets{"lb.com"},
}, },
}, },
}, },
@ -272,11 +264,11 @@ func testIngressEndpoints(t *testing.T) {
expected: []*endpoint.Endpoint{ expected: []*endpoint.Endpoint{
{ {
DNSName: "example.org", DNSName: "example.org",
Target: "8.8.8.8", Targets: endpoint.Targets{"8.8.8.8"},
}, },
{ {
DNSName: "new.org", DNSName: "new.org",
Target: "lb.com", Targets: endpoint.Targets{"lb.com"},
}, },
}, },
}, },
@ -300,7 +292,7 @@ func testIngressEndpoints(t *testing.T) {
expected: []*endpoint.Endpoint{ expected: []*endpoint.Endpoint{
{ {
DNSName: "example.org", DNSName: "example.org",
Target: "8.8.8.8", Targets: endpoint.Targets{"8.8.8.8"},
}, },
}, },
}, },
@ -322,7 +314,7 @@ func testIngressEndpoints(t *testing.T) {
expected: []*endpoint.Endpoint{ expected: []*endpoint.Endpoint{
{ {
DNSName: "example.org", DNSName: "example.org",
Target: "8.8.8.8", Targets: endpoint.Targets{"8.8.8.8"},
}, },
}, },
}, },
@ -379,7 +371,7 @@ func testIngressEndpoints(t *testing.T) {
expected: []*endpoint.Endpoint{ expected: []*endpoint.Endpoint{
{ {
DNSName: "example.org", DNSName: "example.org",
Target: "8.8.8.8", Targets: endpoint.Targets{"8.8.8.8"},
}, },
}, },
}, },
@ -417,7 +409,7 @@ func testIngressEndpoints(t *testing.T) {
expected: []*endpoint.Endpoint{ expected: []*endpoint.Endpoint{
{ {
DNSName: "example.org", DNSName: "example.org",
Target: "8.8.8.8", Targets: endpoint.Targets{"8.8.8.8"},
}, },
}, },
}, },
@ -455,11 +447,11 @@ func testIngressEndpoints(t *testing.T) {
expected: []*endpoint.Endpoint{ expected: []*endpoint.Endpoint{
{ {
DNSName: "fake1.ext-dns.test.com", DNSName: "fake1.ext-dns.test.com",
Target: "8.8.8.8", Targets: endpoint.Targets{"8.8.8.8"},
}, },
{ {
DNSName: "fake1.ext-dns.test.com", DNSName: "fake1.ext-dns.test.com",
Target: "elb.com", Targets: endpoint.Targets{"elb.com"},
}, },
}, },
fqdnTemplate: "{{.Name}}.ext-dns.test.com", fqdnTemplate: "{{.Name}}.ext-dns.test.com",
@ -516,17 +508,17 @@ func testIngressEndpoints(t *testing.T) {
expected: []*endpoint.Endpoint{ expected: []*endpoint.Endpoint{
{ {
DNSName: "example.org", DNSName: "example.org",
Target: "ingress-target.com", Targets: endpoint.Targets{"ingress-target.com"},
RecordType: endpoint.RecordTypeCNAME, RecordType: endpoint.RecordTypeCNAME,
}, },
{ {
DNSName: "example2.org", DNSName: "example2.org",
Target: "ingress-target.com", Targets: endpoint.Targets{"ingress-target.com"},
RecordType: endpoint.RecordTypeCNAME, RecordType: endpoint.RecordTypeCNAME,
}, },
{ {
DNSName: "example3.org", DNSName: "example3.org",
Target: "1.2.3.4", Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
}, },
}, },
@ -559,12 +551,12 @@ func testIngressEndpoints(t *testing.T) {
expected: []*endpoint.Endpoint{ expected: []*endpoint.Endpoint{
{ {
DNSName: "example.org", DNSName: "example.org",
Target: "ingress-target.com", Targets: endpoint.Targets{"ingress-target.com"},
RecordTTL: endpoint.TTL(6), RecordTTL: endpoint.TTL(6),
}, },
{ {
DNSName: "example2.org", DNSName: "example2.org",
Target: "ingress-target.com", Targets: endpoint.Targets{"ingress-target.com"},
RecordTTL: endpoint.TTL(1), RecordTTL: endpoint.TTL(1),
}, },
}, },
@ -606,17 +598,17 @@ func testIngressEndpoints(t *testing.T) {
expected: []*endpoint.Endpoint{ expected: []*endpoint.Endpoint{
{ {
DNSName: "fake1.ext-dns.test.com", DNSName: "fake1.ext-dns.test.com",
Target: "ingress-target.com", Targets: endpoint.Targets{"ingress-target.com"},
RecordType: endpoint.RecordTypeCNAME, RecordType: endpoint.RecordTypeCNAME,
}, },
{ {
DNSName: "fake2.ext-dns.test.com", DNSName: "fake2.ext-dns.test.com",
Target: "ingress-target.com", Targets: endpoint.Targets{"ingress-target.com"},
RecordType: endpoint.RecordTypeCNAME, RecordType: endpoint.RecordTypeCNAME,
}, },
{ {
DNSName: "fake3.ext-dns.test.com", DNSName: "fake3.ext-dns.test.com",
Target: "1.2.3.4", Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
}, },
}, },

View File

@ -40,8 +40,8 @@ func testMultiSourceImplementsSource(t *testing.T) {
// testMultiSourceEndpoints tests merged endpoints from children are returned. // testMultiSourceEndpoints tests merged endpoints from children are returned.
func testMultiSourceEndpoints(t *testing.T) { func testMultiSourceEndpoints(t *testing.T) {
foo := &endpoint.Endpoint{DNSName: "foo", Target: "8.8.8.8"} foo := &endpoint.Endpoint{DNSName: "foo", Targets: endpoint.Targets{"8.8.8.8"}}
bar := &endpoint.Endpoint{DNSName: "bar", Target: "8.8.4.4"} bar := &endpoint.Endpoint{DNSName: "bar", Targets: endpoint.Targets{"8.8.4.4"}}
for _, tc := range []struct { for _, tc := range []struct {
title string title string

View File

@ -19,6 +19,7 @@ package source
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"sort"
"strings" "strings"
"text/template" "text/template"
@ -32,6 +33,10 @@ import (
"github.com/kubernetes-incubator/external-dns/endpoint" "github.com/kubernetes-incubator/external-dns/endpoint"
) )
const (
defaultTargetsCapacity = 10
)
// serviceSource is an implementation of Source for Kubernetes service objects. // serviceSource is an implementation of Source for Kubernetes service objects.
// It will find all services that are under our jurisdiction, i.e. annotated // It will find all services that are under our jurisdiction, i.e. annotated
// desired hostname and matching or no controller annotation. For each of the // desired hostname and matching or no controller annotation. For each of the
@ -119,10 +124,14 @@ func (sc *serviceSource) Endpoints() ([]*endpoint.Endpoint, error) {
endpoints = append(endpoints, svcEndpoints...) endpoints = append(endpoints, svcEndpoints...)
} }
for _, ep := range endpoints {
sort.Sort(ep.Targets)
}
return endpoints, nil return endpoints, nil
} }
func (sc *serviceSource) extractHeadlessEndpoint(svc *v1.Service, hostname string) []*endpoint.Endpoint { func (sc *serviceSource) extractHeadlessEndpoints(svc *v1.Service, hostname string) []*endpoint.Endpoint {
var endpoints []*endpoint.Endpoint var endpoints []*endpoint.Endpoint
@ -138,6 +147,7 @@ func (sc *serviceSource) extractHeadlessEndpoint(svc *v1.Service, hostname strin
if v.Spec.Hostname != "" { if v.Spec.Hostname != "" {
headlessDomain = v.Spec.Hostname + "." + headlessDomain headlessDomain = v.Spec.Hostname + "." + headlessDomain
} }
log.Debugf("Generating matching endpoint %s with HostIP %s", headlessDomain, v.Status.HostIP) log.Debugf("Generating matching endpoint %s with HostIP %s", headlessDomain, v.Status.HostIP)
// To reduce traffice on the DNS API only add record for running Pods. Good Idea? // To reduce traffice on the DNS API only add record for running Pods. Good Idea?
if v.Status.Phase == v1.PodRunning { if v.Status.Phase == v1.PodRunning {
@ -221,54 +231,82 @@ func (sc *serviceSource) setResourceLabel(service v1.Service, endpoints []*endpo
} }
func (sc *serviceSource) generateEndpoints(svc *v1.Service, hostname string) []*endpoint.Endpoint { func (sc *serviceSource) generateEndpoints(svc *v1.Service, hostname string) []*endpoint.Endpoint {
var endpoints []*endpoint.Endpoint
hostname = strings.TrimSuffix(hostname, ".") hostname = strings.TrimSuffix(hostname, ".")
ttl, err := getTTLFromAnnotations(svc.Annotations)
if err != nil {
log.Warn(err)
}
epA := &endpoint.Endpoint{
RecordTTL: ttl,
RecordType: endpoint.RecordTypeA,
Labels: endpoint.NewLabels(),
Targets: make(endpoint.Targets, 0, defaultTargetsCapacity),
DNSName: hostname,
}
epCNAME := &endpoint.Endpoint{
RecordTTL: ttl,
RecordType: endpoint.RecordTypeCNAME,
Labels: endpoint.NewLabels(),
Targets: make(endpoint.Targets, 0, defaultTargetsCapacity),
DNSName: hostname,
}
var endpoints []*endpoint.Endpoint
var targets endpoint.Targets
switch svc.Spec.Type { switch svc.Spec.Type {
case v1.ServiceTypeLoadBalancer: case v1.ServiceTypeLoadBalancer:
endpoints = append(endpoints, extractLoadBalancerEndpoints(svc, hostname)...) targets = append(targets, extractLoadBalancerTargets(svc)...)
case v1.ServiceTypeClusterIP: case v1.ServiceTypeClusterIP:
if sc.publishInternal { if sc.publishInternal {
endpoints = append(endpoints, extractServiceIps(svc, hostname)...) targets = append(targets, extractServiceIps(svc)...)
} }
if svc.Spec.ClusterIP == v1.ClusterIPNone { if svc.Spec.ClusterIP == v1.ClusterIPNone {
endpoints = append(endpoints, sc.extractHeadlessEndpoint(svc, hostname)...) endpoints = append(endpoints, sc.extractHeadlessEndpoints(svc, hostname)...)
} }
} }
for _, t := range targets {
if suitableType(t) == endpoint.RecordTypeA {
epA.Targets = append(epA.Targets, t)
}
if suitableType(t) == endpoint.RecordTypeCNAME {
epCNAME.Targets = append(epCNAME.Targets, t)
}
}
if len(epA.Targets) > 0 {
endpoints = append(endpoints, epA)
}
if len(epCNAME.Targets) > 0 {
endpoints = append(endpoints, epCNAME)
}
return endpoints return endpoints
} }
func extractServiceIps(svc *v1.Service, hostname string) []*endpoint.Endpoint { func extractServiceIps(svc *v1.Service) endpoint.Targets {
ttl, err := getTTLFromAnnotations(svc.Annotations)
if err != nil {
log.Warn(err)
}
if svc.Spec.ClusterIP == v1.ClusterIPNone { if svc.Spec.ClusterIP == v1.ClusterIPNone {
log.Debugf("Unable to associate %s headless service with a Cluster IP", svc.Name) log.Debugf("Unable to associate %s headless service with a Cluster IP", svc.Name)
return []*endpoint.Endpoint{} return endpoint.Targets{}
} }
return endpoint.Targets{svc.Spec.ClusterIP}
return []*endpoint.Endpoint{endpoint.NewEndpointWithTTL(hostname, svc.Spec.ClusterIP, endpoint.RecordTypeA, ttl)}
} }
func extractLoadBalancerEndpoints(svc *v1.Service, hostname string) []*endpoint.Endpoint { func extractLoadBalancerTargets(svc *v1.Service) endpoint.Targets {
var endpoints []*endpoint.Endpoint var targets endpoint.Targets
ttl, err := getTTLFromAnnotations(svc.Annotations)
if err != nil {
log.Warn(err)
}
// Create a corresponding endpoint for each configured external entrypoint. // Create a corresponding endpoint for each configured external entrypoint.
for _, lb := range svc.Status.LoadBalancer.Ingress { for _, lb := range svc.Status.LoadBalancer.Ingress {
if lb.IP != "" { if lb.IP != "" {
//TODO(ideahitme): consider retrieving record type from resource annotation instead of empty targets = append(targets, lb.IP)
endpoints = append(endpoints, endpoint.NewEndpointWithTTL(hostname, lb.IP, endpoint.RecordTypeA, ttl))
} }
if lb.Hostname != "" { if lb.Hostname != "" {
endpoints = append(endpoints, endpoint.NewEndpointWithTTL(hostname, lb.Hostname, endpoint.RecordTypeCNAME, ttl)) targets = append(targets, lb.Hostname)
} }
} }
return endpoints return targets
} }

View File

@ -191,7 +191,7 @@ func testServiceSourceEndpoints(t *testing.T) {
"", "",
[]string{"1.2.3.4"}, []string{"1.2.3.4"},
[]*endpoint.Endpoint{ []*endpoint.Endpoint{
{DNSName: "foo.example.org", Target: "1.2.3.4"}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
}, },
false, false,
}, },
@ -229,8 +229,8 @@ func testServiceSourceEndpoints(t *testing.T) {
"", "",
[]string{"1.2.3.4"}, []string{"1.2.3.4"},
[]*endpoint.Endpoint{ []*endpoint.Endpoint{
{DNSName: "foo.example.org", Target: "1.2.3.4"}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
{DNSName: "bar.example.org", Target: "1.2.3.4"}, {DNSName: "bar.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
}, },
false, false,
}, },
@ -250,8 +250,8 @@ func testServiceSourceEndpoints(t *testing.T) {
"", "",
[]string{"1.2.3.4"}, []string{"1.2.3.4"},
[]*endpoint.Endpoint{ []*endpoint.Endpoint{
{DNSName: "foo.example.org", Target: "1.2.3.4"}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
{DNSName: "bar.example.org", Target: "1.2.3.4"}, {DNSName: "bar.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
}, },
false, false,
}, },
@ -271,7 +271,7 @@ func testServiceSourceEndpoints(t *testing.T) {
"", "",
[]string{"lb.example.com"}, // Kubernetes omits the trailing dot []string{"lb.example.com"}, // Kubernetes omits the trailing dot
[]*endpoint.Endpoint{ []*endpoint.Endpoint{
{DNSName: "foo.example.org", Target: "lb.example.com"}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"lb.example.com"}},
}, },
false, false,
}, },
@ -291,8 +291,8 @@ func testServiceSourceEndpoints(t *testing.T) {
"", "",
[]string{"1.2.3.4", "lb.example.com"}, // Kubernetes omits the trailing dot []string{"1.2.3.4", "lb.example.com"}, // Kubernetes omits the trailing dot
[]*endpoint.Endpoint{ []*endpoint.Endpoint{
{DNSName: "foo.example.org", Target: "1.2.3.4"}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
{DNSName: "foo.example.org", Target: "lb.example.com"}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"lb.example.com"}},
}, },
false, false,
}, },
@ -313,7 +313,7 @@ func testServiceSourceEndpoints(t *testing.T) {
"", "",
[]string{"1.2.3.4"}, []string{"1.2.3.4"},
[]*endpoint.Endpoint{ []*endpoint.Endpoint{
{DNSName: "foo.example.org", Target: "1.2.3.4"}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
}, },
false, false,
}, },
@ -352,7 +352,7 @@ func testServiceSourceEndpoints(t *testing.T) {
"", "",
[]string{"1.2.3.4"}, []string{"1.2.3.4"},
[]*endpoint.Endpoint{ []*endpoint.Endpoint{
{DNSName: "foo.example.org", Target: "1.2.3.4"}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
}, },
false, false,
}, },
@ -390,7 +390,7 @@ func testServiceSourceEndpoints(t *testing.T) {
"", "",
[]string{"1.2.3.4"}, []string{"1.2.3.4"},
[]*endpoint.Endpoint{ []*endpoint.Endpoint{
{DNSName: "foo.example.org", Target: "1.2.3.4"}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
}, },
false, false,
}, },
@ -411,7 +411,7 @@ func testServiceSourceEndpoints(t *testing.T) {
"", "",
[]string{"1.2.3.4"}, []string{"1.2.3.4"},
[]*endpoint.Endpoint{ []*endpoint.Endpoint{
{DNSName: "foo.example.org", Target: "1.2.3.4"}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
}, },
false, false,
}, },
@ -470,7 +470,7 @@ func testServiceSourceEndpoints(t *testing.T) {
"", "",
[]string{"1.2.3.4"}, []string{"1.2.3.4"},
[]*endpoint.Endpoint{ []*endpoint.Endpoint{
{DNSName: "foo.example.org", Target: "1.2.3.4"}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
}, },
false, false,
}, },
@ -512,7 +512,7 @@ func testServiceSourceEndpoints(t *testing.T) {
false, false,
}, },
{ {
"multiple external entrypoints return multiple endpoints", "multiple external entrypoints return a single endpoint with multiple targets",
"", "",
"", "",
"testing", "testing",
@ -527,8 +527,7 @@ func testServiceSourceEndpoints(t *testing.T) {
"", "",
[]string{"1.2.3.4", "8.8.8.8"}, []string{"1.2.3.4", "8.8.8.8"},
[]*endpoint.Endpoint{ []*endpoint.Endpoint{
{DNSName: "foo.example.org", Target: "1.2.3.4"}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4", "8.8.8.8"}},
{DNSName: "foo.example.org", Target: "8.8.8.8"},
}, },
false, false,
}, },
@ -566,7 +565,7 @@ func testServiceSourceEndpoints(t *testing.T) {
"", "",
[]string{"1.2.3.4"}, []string{"1.2.3.4"},
[]*endpoint.Endpoint{ []*endpoint.Endpoint{
{DNSName: "foo.example.org", Target: "1.2.3.4"}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
}, },
false, false,
}, },
@ -588,8 +587,8 @@ func testServiceSourceEndpoints(t *testing.T) {
"", "",
[]string{"1.2.3.4"}, []string{"1.2.3.4"},
[]*endpoint.Endpoint{ []*endpoint.Endpoint{
{DNSName: "foo.example.org", Target: "1.2.3.4"}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
{DNSName: "bar.example.org", Target: "1.2.3.4"}, {DNSName: "bar.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
}, },
false, false,
}, },
@ -607,8 +606,8 @@ func testServiceSourceEndpoints(t *testing.T) {
"", "",
[]string{"1.2.3.4", "elb.com"}, []string{"1.2.3.4", "elb.com"},
[]*endpoint.Endpoint{ []*endpoint.Endpoint{
{DNSName: "foo.bar.example.com", Target: "1.2.3.4"}, {DNSName: "foo.bar.example.com", Targets: endpoint.Targets{"1.2.3.4"}},
{DNSName: "foo.bar.example.com", Target: "elb.com"}, {DNSName: "foo.bar.example.com", Targets: endpoint.Targets{"elb.com"}},
}, },
false, false,
}, },
@ -628,8 +627,8 @@ func testServiceSourceEndpoints(t *testing.T) {
"", "",
[]string{"1.2.3.4", "elb.com"}, []string{"1.2.3.4", "elb.com"},
[]*endpoint.Endpoint{ []*endpoint.Endpoint{
{DNSName: "foo.example.org", Target: "1.2.3.4"}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
{DNSName: "foo.example.org", Target: "elb.com"}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"elb.com"}},
}, },
false, false,
}, },
@ -649,7 +648,7 @@ func testServiceSourceEndpoints(t *testing.T) {
"", "",
[]string{"1.2.3.4"}, []string{"1.2.3.4"},
[]*endpoint.Endpoint{ []*endpoint.Endpoint{
{DNSName: "mate.example.org", Target: "1.2.3.4"}, {DNSName: "mate.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
}, },
false, false,
}, },
@ -685,7 +684,7 @@ func testServiceSourceEndpoints(t *testing.T) {
"", "",
[]string{"1.2.3.4"}, []string{"1.2.3.4"},
[]*endpoint.Endpoint{ []*endpoint.Endpoint{
{DNSName: "foo.example.org", Target: "1.2.3.4", RecordTTL: endpoint.TTL(0)}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}, RecordTTL: endpoint.TTL(0)},
}, },
false, false,
}, },
@ -706,7 +705,7 @@ func testServiceSourceEndpoints(t *testing.T) {
"", "",
[]string{"1.2.3.4"}, []string{"1.2.3.4"},
[]*endpoint.Endpoint{ []*endpoint.Endpoint{
{DNSName: "foo.example.org", Target: "1.2.3.4", RecordTTL: endpoint.TTL(0)}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}, RecordTTL: endpoint.TTL(0)},
}, },
false, false,
}, },
@ -727,7 +726,7 @@ func testServiceSourceEndpoints(t *testing.T) {
"", "",
[]string{"1.2.3.4"}, []string{"1.2.3.4"},
[]*endpoint.Endpoint{ []*endpoint.Endpoint{
{DNSName: "foo.example.org", Target: "1.2.3.4", RecordTTL: endpoint.TTL(10)}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}, RecordTTL: endpoint.TTL(10)},
}, },
false, false,
}, },
@ -748,7 +747,7 @@ func testServiceSourceEndpoints(t *testing.T) {
"", "",
[]string{"1.2.3.4"}, []string{"1.2.3.4"},
[]*endpoint.Endpoint{ []*endpoint.Endpoint{
{DNSName: "foo.example.org", Target: "1.2.3.4", RecordTTL: endpoint.TTL(0)}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}, RecordTTL: endpoint.TTL(0)},
}, },
false, false,
}, },
@ -846,7 +845,7 @@ func TestClusterIpServices(t *testing.T) {
"1.2.3.4", "1.2.3.4",
[]string{}, []string{},
[]*endpoint.Endpoint{ []*endpoint.Endpoint{
{DNSName: "foo.example.org", Target: "1.2.3.4"}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
}, },
false, false,
}, },
@ -864,7 +863,7 @@ func TestClusterIpServices(t *testing.T) {
"4.5.6.7", "4.5.6.7",
[]string{}, []string{},
[]*endpoint.Endpoint{ []*endpoint.Endpoint{
{DNSName: "foo.bar.example.com", Target: "4.5.6.7"}, {DNSName: "foo.bar.example.com", Targets: endpoint.Targets{"4.5.6.7"}},
}, },
false, false,
}, },
@ -988,8 +987,8 @@ func TestHeadlessServices(t *testing.T) {
[]string{"foo-0", "foo-1"}, []string{"foo-0", "foo-1"},
[]v1.PodPhase{v1.PodRunning, v1.PodRunning}, []v1.PodPhase{v1.PodRunning, v1.PodRunning},
[]*endpoint.Endpoint{ []*endpoint.Endpoint{
{DNSName: "foo-0.service.example.org", Target: "1.1.1.1"}, {DNSName: "foo-0.service.example.org", Targets: endpoint.Targets{"1.1.1.1"}},
{DNSName: "foo-1.service.example.org", Target: "1.1.1.1"}, {DNSName: "foo-1.service.example.org", Targets: endpoint.Targets{"1.1.1.1"}},
}, },
false, false,
}, },
@ -1015,7 +1014,7 @@ func TestHeadlessServices(t *testing.T) {
[]string{"foo-0", "foo-1"}, []string{"foo-0", "foo-1"},
[]v1.PodPhase{v1.PodRunning, v1.PodFailed}, []v1.PodPhase{v1.PodRunning, v1.PodFailed},
[]*endpoint.Endpoint{ []*endpoint.Endpoint{
{DNSName: "foo-0.service.example.org", Target: "1.1.1.1"}, {DNSName: "foo-0.service.example.org", Targets: endpoint.Targets{"1.1.1.1"}},
}, },
false, false,
}, },
@ -1041,8 +1040,8 @@ func TestHeadlessServices(t *testing.T) {
[]string{"", ""}, []string{"", ""},
[]v1.PodPhase{v1.PodRunning, v1.PodRunning}, []v1.PodPhase{v1.PodRunning, v1.PodRunning},
[]*endpoint.Endpoint{ []*endpoint.Endpoint{
{DNSName: "service.example.org", Target: "1.1.1.1"}, {DNSName: "service.example.org", Targets: endpoint.Targets{"1.1.1.1"}},
{DNSName: "service.example.org", Target: "1.1.1.1"}, {DNSName: "service.example.org", Targets: endpoint.Targets{"1.1.1.1"}},
}, },
false, false,
}, },

View File

@ -39,8 +39,8 @@ func validateEndpoint(t *testing.T, endpoint, expected *endpoint.Endpoint) {
t.Errorf("expected %s, got %s", expected.DNSName, endpoint.DNSName) t.Errorf("expected %s, got %s", expected.DNSName, endpoint.DNSName)
} }
if endpoint.Target != expected.Target { if !endpoint.Targets.Same(expected.Targets) {
t.Errorf("expected %s, got %s", expected.Target, endpoint.Target) t.Errorf("expected %s, got %s", expected.Targets, endpoint.Targets)
} }
if endpoint.RecordTTL != expected.RecordTTL { if endpoint.RecordTTL != expected.RecordTTL {