diff --git a/endpoint/endpoint.go b/endpoint/endpoint.go index f7af843f6..9c7107a37 100644 --- a/endpoint/endpoint.go +++ b/endpoint/endpoint.go @@ -374,3 +374,24 @@ type DNSEndpointList struct { metav1.ListMeta `json:"metadata,omitempty"` Items []DNSEndpoint `json:"items"` } + +// RemoveDuplicates returns a slice holding the unique endpoints. +// This function doesn't contemplate the Targets of an Endpoint +// as part of the primary Key +func RemoveDuplicates(endpoints []*Endpoint) []*Endpoint { + visited := make(map[EndpointKey]struct{}) + result := []*Endpoint{} + + for _, ep := range endpoints { + key := ep.Key() + + if _, found := visited[key]; !found { + result = append(result, ep) + visited[key] = struct{}{} + } else { + log.Debugf(`Skipping duplicated endpoint: %v`, ep) + } + } + + return result +} diff --git a/endpoint/endpoint_test.go b/endpoint/endpoint_test.go index a3b3399cf..0d87c2bbe 100644 --- a/endpoint/endpoint_test.go +++ b/endpoint/endpoint_test.go @@ -252,3 +252,136 @@ func TestIsOwnedBy(t *testing.T) { }) } } + +func TestDuplicatedEndpointsWithSimpleZone(t *testing.T) { + foo1 := &Endpoint{ + DNSName: "foo.com", + RecordType: RecordTypeA, + Labels: Labels{ + OwnerLabelKey: "foo", + }, + } + foo2 := &Endpoint{ + DNSName: "foo.com", + RecordType: RecordTypeA, + Labels: Labels{ + OwnerLabelKey: "foo", + }, + } + bar := &Endpoint{ + DNSName: "foo.com", + RecordType: RecordTypeA, + Labels: Labels{ + OwnerLabelKey: "bar", + }, + } + + type args struct { + eps []*Endpoint + } + tests := []struct { + name string + args args + want []*Endpoint + }{ + { + name: "filter values", + args: args{ + eps: []*Endpoint{ + foo1, + foo2, + bar, + }, + }, + want: []*Endpoint{ + foo1, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := RemoveDuplicates(tt.args.eps); !reflect.DeepEqual(got, tt.want) { + t.Errorf("RemoveDuplicates() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestDuplicatedEndpointsWithOverlappingZones(t *testing.T) { + foo1 := &Endpoint{ + DNSName: "internal.foo.com", + RecordType: RecordTypeA, + Labels: Labels{ + OwnerLabelKey: "foo", + }, + } + foo2 := &Endpoint{ + DNSName: "internal.foo.com", + RecordType: RecordTypeA, + Labels: Labels{ + OwnerLabelKey: "foo", + }, + } + foo3 := &Endpoint{ + DNSName: "foo.com", + RecordType: RecordTypeA, + Labels: Labels{ + OwnerLabelKey: "foo", + }, + } + foo4 := &Endpoint{ + DNSName: "foo.com", + RecordType: RecordTypeA, + Labels: Labels{ + OwnerLabelKey: "foo", + }, + } + bar := &Endpoint{ + DNSName: "internal.foo.com", + RecordType: RecordTypeA, + Labels: Labels{ + OwnerLabelKey: "bar", + }, + } + bar2 := &Endpoint{ + DNSName: "foo.com", + RecordType: RecordTypeA, + Labels: Labels{ + OwnerLabelKey: "bar", + }, + } + + type args struct { + eps []*Endpoint + } + tests := []struct { + name string + args args + want []*Endpoint + }{ + { + name: "filter values", + args: args{ + eps: []*Endpoint{ + foo1, + foo2, + foo3, + foo4, + bar, + bar2, + }, + }, + want: []*Endpoint{ + foo1, + foo3, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := RemoveDuplicates(tt.args.eps); !reflect.DeepEqual(got, tt.want) { + t.Errorf("RemoveDuplicates() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/plan/plan.go b/plan/plan.go index b4100328d..d0ca7f96a 100644 --- a/plan/plan.go +++ b/plan/plan.go @@ -252,6 +252,7 @@ func (p *Plan) Calculate() *Plan { // filter out updates this external dns does not have ownership claim over if p.OwnerID != "" { changes.Delete = endpoint.FilterEndpointsByOwnerID(p.OwnerID, changes.Delete) + changes.Delete = endpoint.RemoveDuplicates(changes.Delete) changes.UpdateOld = endpoint.FilterEndpointsByOwnerID(p.OwnerID, changes.UpdateOld) changes.UpdateNew = endpoint.FilterEndpointsByOwnerID(p.OwnerID, changes.UpdateNew) }