Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rfc2136: merge Endpoints with same name/type into single Endpoint #4613

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions charts/external-dns/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Added support for `extraContainers` argument. ([#4432](https://github.com/kubernetes-sigs/external-dns/pull/4432)) _@omerap12_
- Added support for setting `excludeDomains` argument. ([#4380](https://github.com/kubernetes-sigs/external-dns/pull/4380)) _@bford-evs_
- Fixed reconciling `A` records that have more than 1 target for `rfc2136` provider. ([4613#](https://github.com/kubernetes-sigs/external-dns/pull/4613)) _@tdyas_
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- Fixed reconciling `A` records that have more than 1 target for `rfc2136` provider. ([4613#](https://github.com/kubernetes-sigs/external-dns/pull/4613)) _@tdyas_

This changelog is for the helm chart


### Changed

Expand Down
33 changes: 33 additions & 0 deletions endpoint/endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,39 @@ type DNSEndpointList struct {
Items []DNSEndpoint `json:"items"`
}

// Given a slice of Endpoint, merge those Endpoints with the same Name and Type into a single Endpoint
// with multiple Targets. Returns a slice of Endpoint with the merged Endpoints.
func MergeEndpointsByNameType(endpoints []*Endpoint) []*Endpoint {
endpointsByNameType := map[string][]*Endpoint{}

for _, e := range endpoints {
key := fmt.Sprintf("%s-%s", e.DNSName, e.RecordType)
endpointsByNameType[key] = append(endpointsByNameType[key], e)
}

// If no merge occurred, just return the existing endpoints.
if len(endpointsByNameType) == len(endpoints) {
return endpoints
}

// Otherwise, construct a new list of endpoints with the endpoints merged.
var result []*Endpoint
for _, endpoints := range endpointsByNameType {
dnsName := endpoints[0].DNSName
recordType := endpoints[0].RecordType

var targets Targets
for _, e := range endpoints {
targets = append(targets, e.Targets...)
}

e := NewEndpoint(dnsName, recordType, targets...)
result = append(result, e)
}

return result
}

// RemoveDuplicates returns a slice holding the unique endpoints.
// This function doesn't contemplate the Targets of an Endpoint
// as part of the primary Key
Expand Down
36 changes: 36 additions & 0 deletions endpoint/endpoint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@ limitations under the License.
package endpoint

import (
"sort"
"reflect"
"testing"

"github.com/stretchr/testify/assert"
)

func TestNewEndpoint(t *testing.T) {
Expand Down Expand Up @@ -114,6 +117,39 @@ func TestSameFailures(t *testing.T) {
}
}

func TestMergeRecordsByNameType(t *testing.T) {
xs := []*Endpoint{
NewEndpoint("foo.example.com", "A", "1.2.3.4"),
NewEndpoint("bar.example.com", "A", "1.2.3.4"),
NewEndpoint("foo.example.com", "A", "5.6.7.8"),
NewEndpoint("foo.example.com", "CNAME", "somewhere.out.there.com"),
}

merged := MergeEndpointsByNameType(xs)

assert.Equal(t, 3, len(merged))
sort.SliceStable(merged, func(i, j int) bool {
if merged[i].DNSName != merged[j].DNSName {
return merged[i].DNSName < merged[j].DNSName
}
return merged[i].RecordType < merged[j].RecordType
})
assert.Equal(t, "bar.example.com", merged[0].DNSName)
assert.Equal(t, "A", merged[0].RecordType)
assert.Equal(t, 1, len(merged[0].Targets))
assert.Equal(t, "1.2.3.4", merged[0].Targets[0])

assert.Equal(t, "foo.example.com", merged[1].DNSName)
assert.Equal(t, "A", merged[1].RecordType)
assert.Equal(t, 2, len(merged[1].Targets))
assert.ElementsMatch(t, []string{"1.2.3.4", "5.6.7.8"}, merged[1].Targets)

assert.Equal(t, "foo.example.com", merged[2].DNSName)
assert.Equal(t, "CNAME", merged[2].RecordType)
assert.Equal(t, 1, len(merged[2].Targets))
assert.Equal(t, "somewhere.out.there.com", merged[2].Targets[0])
}

func TestIsLess(t *testing.T) {
testsA := []Targets{
{""},
Expand Down
34 changes: 1 addition & 33 deletions provider/digitalocean/digital_ocean.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,38 +116,6 @@ func (p *DigitalOceanProvider) Zones(ctx context.Context) ([]godo.Domain, error)
return result, nil
}

// Merge Endpoints with the same Name and Type into a single endpoint with multiple Targets.
func mergeEndpointsByNameType(endpoints []*endpoint.Endpoint) []*endpoint.Endpoint {
endpointsByNameType := map[string][]*endpoint.Endpoint{}

for _, e := range endpoints {
key := fmt.Sprintf("%s-%s", e.DNSName, e.RecordType)
endpointsByNameType[key] = append(endpointsByNameType[key], e)
}

// If no merge occurred, just return the existing endpoints.
if len(endpointsByNameType) == len(endpoints) {
return endpoints
}

// Otherwise, construct a new list of endpoints with the endpoints merged.
var result []*endpoint.Endpoint
for _, endpoints := range endpointsByNameType {
dnsName := endpoints[0].DNSName
recordType := endpoints[0].RecordType

targets := make([]string, len(endpoints))
for i, e := range endpoints {
targets[i] = e.Targets[0]
}

e := endpoint.NewEndpoint(dnsName, recordType, targets...)
result = append(result, e)
}

return result
}

// Records returns the list of records in a given zone.
func (p *DigitalOceanProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, error) {
zones, err := p.Zones(ctx)
Expand Down Expand Up @@ -181,7 +149,7 @@ func (p *DigitalOceanProvider) Records(ctx context.Context) ([]*endpoint.Endpoin

// Merge endpoints with the same name and type (e.g., multiple A records for a single
// DNS name) into one endpoint with multiple targets.
endpoints = mergeEndpointsByNameType(endpoints)
endpoints = endpoint.MergeEndpointsByNameType(endpoints)

// Log the endpoints that were found.
log.WithFields(log.Fields{
Expand Down
34 changes: 0 additions & 34 deletions provider/digitalocean/digital_ocean_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
"fmt"
"os"
"reflect"
"sort"
"testing"

"github.com/digitalocean/godo"
Expand Down Expand Up @@ -671,36 +670,3 @@ func TestDigitalOceanAllRecords(t *testing.T) {
t.Errorf("expected to fail, %s", err)
}
}

func TestDigitalOceanMergeRecordsByNameType(t *testing.T) {
xs := []*endpoint.Endpoint{
endpoint.NewEndpoint("foo.example.com", "A", "1.2.3.4"),
endpoint.NewEndpoint("bar.example.com", "A", "1.2.3.4"),
endpoint.NewEndpoint("foo.example.com", "A", "5.6.7.8"),
endpoint.NewEndpoint("foo.example.com", "CNAME", "somewhere.out.there.com"),
}

merged := mergeEndpointsByNameType(xs)

assert.Equal(t, 3, len(merged))
sort.SliceStable(merged, func(i, j int) bool {
if merged[i].DNSName != merged[j].DNSName {
return merged[i].DNSName < merged[j].DNSName
}
return merged[i].RecordType < merged[j].RecordType
})
assert.Equal(t, "bar.example.com", merged[0].DNSName)
assert.Equal(t, "A", merged[0].RecordType)
assert.Equal(t, 1, len(merged[0].Targets))
assert.Equal(t, "1.2.3.4", merged[0].Targets[0])

assert.Equal(t, "foo.example.com", merged[1].DNSName)
assert.Equal(t, "A", merged[1].RecordType)
assert.Equal(t, 2, len(merged[1].Targets))
assert.ElementsMatch(t, []string{"1.2.3.4", "5.6.7.8"}, merged[1].Targets)

assert.Equal(t, "foo.example.com", merged[2].DNSName)
assert.Equal(t, "CNAME", merged[2].RecordType)
assert.Equal(t, 1, len(merged[2].Targets))
assert.Equal(t, "somewhere.out.there.com", merged[2].Targets[0])
}
2 changes: 2 additions & 0 deletions provider/rfc2136/rfc2136.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,8 @@ OuterLoop:
eps = append(eps, ep)
}

eps = endpoint.MergeEndpointsByNameType(eps)

return eps, nil
}

Expand Down
Loading