Skip to content
Closed
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
2 changes: 1 addition & 1 deletion controller/execute.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ func buildProvider(
case "dnsimple":
p, err = dnsimple.NewDnsimpleProvider(domainFilter, zoneIDFilter, cfg.DryRun)
case "coredns", "skydns":
p, err = coredns.NewCoreDNSProvider(domainFilter, cfg.CoreDNSPrefix, cfg.DryRun)
p, err = coredns.NewCoreDNSProvider(domainFilter, cfg.CoreDNSPrefix, cfg.TXTOwnerID, cfg.DryRun)
case "exoscale":
p, err = exoscale.NewExoscaleProvider(
cfg.ExoscaleAPIEnvironment,
Expand Down
31 changes: 21 additions & 10 deletions provider/coredns/coredns.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ type coreDNSProvider struct {
coreDNSPrefix string
domainFilter *endpoint.DomainFilter
client coreDNSClient
setIdentifier string
}

// Service represents CoreDNS etcd record
Expand All @@ -84,6 +85,9 @@ type Service struct {

// Etcd key where we found this service and ignored from json un-/marshaling
Key string `json:"-"`

// SetIdentifier allows to distinguish between multiple clusters of external-dns (only external-dns)
SetIdentifier string `json:"setIdentifier,omitempty"`
}

type etcdClient struct {
Expand Down Expand Up @@ -196,7 +200,7 @@ func newETCDClient() (coreDNSClient, error) {
}

// NewCoreDNSProvider is a CoreDNS provider constructor
func NewCoreDNSProvider(domainFilter *endpoint.DomainFilter, prefix string, dryRun bool) (provider.Provider, error) {
func NewCoreDNSProvider(domainFilter *endpoint.DomainFilter, prefix, setIdentifier string, dryRun bool) (provider.Provider, error) {
client, err := newETCDClient()
if err != nil {
return nil, err
Expand All @@ -207,14 +211,15 @@ func NewCoreDNSProvider(domainFilter *endpoint.DomainFilter, prefix string, dryR
dryRun: dryRun,
coreDNSPrefix: prefix,
domainFilter: domainFilter,
setIdentifier: setIdentifier,
}, nil
}

// findEp takes an Endpoint slice and looks for an element in it. If found it will
// return Endpoint, otherwise it will return nil and a bool of false.
func findEp(slice []*endpoint.Endpoint, dnsName string) (*endpoint.Endpoint, bool) {
func findEp(slice []*endpoint.Endpoint, dnsName string, setIdentifier string) (*endpoint.Endpoint, bool) {
for _, item := range slice {
if item.DNSName == dnsName {
if item.DNSName == dnsName && (item.SetIdentifier == setIdentifier || item.SetIdentifier == "") { // empty string is used to migrate to set identifier
return item, true
}
}
Expand Down Expand Up @@ -250,7 +255,7 @@ func (p coreDNSProvider) Records(_ context.Context) ([]*endpoint.Endpoint, error
log.Debugf("Getting service (%v) with service host (%s)", service, service.Host)
prefix := strings.Join(domains[:service.TargetStrip], ".")
if service.Host != "" {
ep, found := findEp(result, dnsName)
ep, found := findEp(result, dnsName, service.SetIdentifier)
if found {
ep.Targets = append(ep.Targets, service.Host)
log.Debugf("Extending ep (%s) with new service host (%s)", ep, service.Host)
Expand All @@ -269,6 +274,7 @@ func (p coreDNSProvider) Records(_ context.Context) ([]*endpoint.Endpoint, error
ep.Labels["originalText"] = service.Text
ep.Labels[randomPrefixLabel] = prefix
ep.Labels[service.Host] = prefix
ep.SetIdentifier = service.SetIdentifier
result = append(result, ep)
}
if service.Text != "" {
Expand All @@ -278,6 +284,7 @@ func (p coreDNSProvider) Records(_ context.Context) ([]*endpoint.Endpoint, error
service.Text,
)
ep.Labels[randomPrefixLabel] = prefix
ep.SetIdentifier = service.SetIdentifier
result = append(result, ep)
}
}
Expand Down Expand Up @@ -355,15 +362,19 @@ func (p coreDNSProvider) createServicesForEndpoint(dnsName string, ep *endpoint.
group = prop
}
service := Service{
Host: target,
Text: ep.Labels["originalText"],
Key: p.etcdKeyFor(prefix + "." + dnsName),
TargetStrip: strings.Count(prefix, ".") + 1,
TTL: uint32(ep.RecordTTL),
Group: group,
Host: target,
Text: ep.Labels["originalText"],
Key: p.etcdKeyFor(prefix + "." + dnsName),
TargetStrip: strings.Count(prefix, ".") + 1,
TTL: uint32(ep.RecordTTL),
Group: group,
SetIdentifier: ep.SetIdentifier,
}
services = append(services, &service)
ep.Labels[target] = prefix
if service.SetIdentifier == "" {
service.SetIdentifier = p.setIdentifier
}
}

// Clean outdated labels
Expand Down
77 changes: 74 additions & 3 deletions provider/coredns/coredns_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@ import (
"reflect"
"strings"
"testing"
"time"

log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"go.etcd.io/etcd/api/v3/mvccpb"
etcdcv3 "go.etcd.io/etcd/client/v3"
"sigs.k8s.io/external-dns/registry"

"sigs.k8s.io/external-dns/endpoint"
"sigs.k8s.io/external-dns/internal/testutils"
Expand Down Expand Up @@ -460,7 +462,7 @@ func validateServices(services map[string]Service, expectedServices map[string][
}
found := false
for i, expectedServiceEntry := range expectedServiceEntries {
if value.Host == expectedServiceEntry.Host && value.Text == expectedServiceEntry.Text && value.Group == expectedServiceEntry.Group {
if value.Host == expectedServiceEntry.Host && value.Text == expectedServiceEntry.Text && value.Group == expectedServiceEntry.Group && value.SetIdentifier == expectedServiceEntry.SetIdentifier {
expectedServiceEntries = append(expectedServiceEntries[:i], expectedServiceEntries[i+1:]...)
found = true
break
Expand Down Expand Up @@ -753,7 +755,7 @@ func TestNewCoreDNSProvider(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
testutils.TestHelperEnvSetter(t, tt.envs)

provider, err := NewCoreDNSProvider(&endpoint.DomainFilter{}, "/prefix/", false)
provider, err := NewCoreDNSProvider(&endpoint.DomainFilter{}, "/prefix/", "", false)
if tt.wantErr {
require.Error(t, err)
assert.EqualError(t, err, tt.errMsg)
Expand Down Expand Up @@ -803,7 +805,7 @@ func TestFindEp(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, ok := findEp(tt.slice, tt.dnsName)
got, ok := findEp(tt.slice, tt.dnsName, "")
assert.Equal(t, tt.wantBool, ok)
if ok {
assert.Equal(t, tt.dnsName, got.DNSName)
Expand Down Expand Up @@ -908,3 +910,72 @@ func TestRecordsAWithGroupServiceTranslation(t *testing.T) {
t.Errorf("got unexpected Group name: %s != %s", prop, "test1")
}
}

func TestApplyChangesWithSetIdentifier(t *testing.T) {
client := fakeETCDClient{
map[string]Service{},
}
coredns := coreDNSProvider{
client: client,
coreDNSPrefix: defaultCoreDNSPrefix,
setIdentifier: "bla",
}

changes1 := &plan.Changes{
Create: []*endpoint.Endpoint{
endpoint.NewEndpoint("domain1.local", endpoint.RecordTypeA, "5.5.5.5").WithSetIdentifier("asd"),
endpoint.NewEndpoint("domain2.local", endpoint.RecordTypeA, "5.5.5.6"),
endpoint.NewEndpoint("domain3.local", endpoint.RecordTypeA, "5.5.5.7"),
},
}
coredns.ApplyChanges(context.Background(), changes1)

expectedServices1 := map[string][]*Service{
"/skydns/local/domain1": {{Host: "5.5.5.5", SetIdentifier: "asd"}},
"/skydns/local/domain2": {{Host: "5.5.5.6", SetIdentifier: "bla"}},
"/skydns/local/domain3": {{Host: "5.5.5.7", SetIdentifier: "bla"}},
}
validateServices(client.services, expectedServices1, t, 1)
}

func TestRecordsWithSetIdentifier(t *testing.T) {
client := fakeETCDClient{
map[string]Service{
"/skydns/local/a-domain1/45bd7d0d": {
Text: "\"heritage=external-dns,external-dns/owner=cluster-a,external-dns/resource=resource1\"",
TargetStrip: 1,
SetIdentifier: "cluster-a",
},
"/skydns/local/a-domain1/56615f35": {
Text: "\"heritage=external-dns,external-dns/owner=cluster-b,external-dns/resource=resource1\"",
TargetStrip: 1,
SetIdentifier: "cluster-b",
},
"/skydns/local/domain1/29afc1cc": {
Host: "10.15.1.1",
Text: "\"heritage=external-dns,external-dns/owner=cluster-b,external-dns/resource=resource1\"",
TargetStrip: 1,
SetIdentifier: "cluster-b",
},
"/skydns/local/domain1/738fb9f0": {
Host: "10.25.1.1",
Text: "\"heritage=external-dns,external-dns/owner=cluster-a,external-dns/resource=resource1\"",
TargetStrip: 1,
SetIdentifier: "cluster-a",
},
},
}
p := coreDNSProvider{
client: client,
coreDNSPrefix: defaultCoreDNSPrefix,
}
r, _ := registry.NewTXTRegistry(p, "", "", "cluster-b", time.Hour, "", []string{}, []string{}, false, nil)
endpoints, err := r.Records(context.Background())
require.NoError(t, err)
assert.Len(t, endpoints, 2)
identifierMap := map[string]bool{}
for _, endpoint := range endpoints {
identifierMap[endpoint.SetIdentifier] = true
}
assert.Len(t, identifierMap, 2)
}