refactor inmemory provider (#199)

* reactor inmemoy provider

* add inmemoryclient to logically split functions

* implement apply changes

* fix all tests

* chore: use bogus value for zone to ensure it's ignored

* chore: use bogus value for zone to ensure it's ignored (2)
This commit is contained in:
Yerken 2017-05-09 13:19:47 +02:00 committed by GitHub
parent a2c893f2bb
commit f11c37c2ee
4 changed files with 351 additions and 402 deletions

View File

@ -18,6 +18,7 @@ package provider
import ( import (
"errors" "errors"
"strings"
"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"
@ -36,12 +37,12 @@ var (
ErrInvalidBatchRequest = errors.New("invalid batch request") ErrInvalidBatchRequest = errors.New("invalid batch request")
) )
type zone map[string][]*InMemoryRecord
// InMemoryProvider - dns provider only used for testing purposes // InMemoryProvider - dns provider only used for testing purposes
// initialized as dns provider with no records // initialized as dns provider with no records
type InMemoryProvider struct { type InMemoryProvider struct {
zones map[string]zone domain string
client *inMemoryClient
filter *filter
OnApplyChanges func(changes *plan.Changes) OnApplyChanges func(changes *plan.Changes)
OnRecords func() OnRecords func()
} }
@ -49,39 +50,42 @@ type InMemoryProvider struct {
// NewInMemoryProvider returns InMemoryProvider DNS provider interface implementation // NewInMemoryProvider returns InMemoryProvider DNS provider interface implementation
func NewInMemoryProvider() *InMemoryProvider { func NewInMemoryProvider() *InMemoryProvider {
return &InMemoryProvider{ return &InMemoryProvider{
zones: map[string]zone{}, filter: &filter{},
OnApplyChanges: func(changes *plan.Changes) {}, OnApplyChanges: func(changes *plan.Changes) {},
OnRecords: func() {}, OnRecords: func() {},
domain: "",
client: newInMemoryClient(),
} }
} }
// InMemoryRecord - record stored in memory
// has additional fields:
// Type - type of string (TODO: Type should probably be part of endpoint struct)
// Payload - string - additional information stored
type InMemoryRecord struct {
Type string
Payload string
*endpoint.Endpoint
}
// CreateZone adds new zone if not present // CreateZone adds new zone if not present
func (im *InMemoryProvider) CreateZone(newZone string) error { func (im *InMemoryProvider) CreateZone(newZone string) error {
if _, exist := im.zones[newZone]; exist { return im.client.CreateZone(newZone)
return ErrZoneAlreadyExists
} }
im.zones[newZone] = zone{}
return nil // Zones returns filtered zones as specified by domain
func (im *InMemoryProvider) Zones() map[string]string {
return im.filter.Zones(im.client.Zones())
} }
// Records returns the list of endpoints // Records returns the list of endpoints
func (im *InMemoryProvider) Records(zone string) ([]*endpoint.Endpoint, error) { func (im *InMemoryProvider) Records(_ string) ([]*endpoint.Endpoint, error) {
defer im.OnRecords() defer im.OnRecords()
if _, exists := im.zones[zone]; !exists { endpoints := make([]*endpoint.Endpoint, 0)
return nil, ErrZoneNotFound
for zoneID := range im.Zones() {
records, err := im.client.Records(zoneID)
if err != nil {
return nil, err
} }
return im.endpoints(zone), nil
for _, record := range records {
endpoints = append(endpoints, endpoint.NewEndpoint(record.Name, record.Target, record.Type))
}
}
return endpoints, nil
} }
// ApplyChanges simply modifies records in memory // ApplyChanges simply modifies records in memory
@ -89,97 +93,229 @@ func (im *InMemoryProvider) Records(zone string) ([]*endpoint.Endpoint, error) {
// create record - record should not exist // create record - record should not exist
// update/delete record - record should exist // update/delete record - record should exist
// create/update/delete lists should not have overlapping records // create/update/delete lists should not have overlapping records
func (im *InMemoryProvider) ApplyChanges(zone string, changes *plan.Changes) error { func (im *InMemoryProvider) ApplyChanges(_ string, changes *plan.Changes) error {
defer im.OnApplyChanges(changes) defer im.OnApplyChanges(changes)
if err := im.validateChangeBatch(zone, changes); err != nil { perZoneChanges := map[string]*plan.Changes{}
return err
zones := im.Zones()
for zoneID := range zones {
perZoneChanges[zoneID] = &plan.Changes{}
} }
for _, newEndpoint := range changes.Create { for _, ep := range changes.Create {
im.zones[zone][newEndpoint.DNSName] = append(im.zones[zone][newEndpoint.DNSName], &InMemoryRecord{ zoneID := im.filter.EndpointZoneID(ep, zones)
Type: suitableType(newEndpoint), perZoneChanges[zoneID].Create = append(perZoneChanges[zoneID].Create, ep)
Endpoint: newEndpoint, }
for _, ep := range changes.UpdateNew {
zoneID := im.filter.EndpointZoneID(ep, zones)
perZoneChanges[zoneID].UpdateNew = append(perZoneChanges[zoneID].UpdateNew, ep)
}
for _, ep := range changes.UpdateOld {
zoneID := im.filter.EndpointZoneID(ep, zones)
perZoneChanges[zoneID].UpdateOld = append(perZoneChanges[zoneID].UpdateOld, ep)
}
for _, ep := range changes.Delete {
zoneID := im.filter.EndpointZoneID(ep, zones)
perZoneChanges[zoneID].Delete = append(perZoneChanges[zoneID].Delete, ep)
}
for zoneID := range perZoneChanges {
change := &inMemoryChange{
Create: convertToInMemoryRecord(perZoneChanges[zoneID].Create),
UpdateNew: convertToInMemoryRecord(perZoneChanges[zoneID].UpdateNew),
UpdateOld: convertToInMemoryRecord(perZoneChanges[zoneID].UpdateOld),
Delete: convertToInMemoryRecord(perZoneChanges[zoneID].Delete),
}
err := im.client.ApplyChanges(zoneID, change)
if err != nil {
return err
}
}
return nil
}
func convertToInMemoryRecord(endpoints []*endpoint.Endpoint) []*inMemoryRecord {
records := []*inMemoryRecord{}
for _, ep := range endpoints {
records = append(records, &inMemoryRecord{
Type: suitableType(ep),
Name: ep.DNSName,
Target: ep.Target,
}) })
} }
return records
}
type filter struct {
domain string
}
// Zones filters map[zoneID]zoneName for names having f.domain as suffix
func (f *filter) Zones(zones map[string]string) map[string]string {
result := map[string]string{}
for zoneID, zoneName := range zones {
if strings.HasSuffix(zoneName, f.domain) {
result[zoneID] = zoneName
}
}
return result
}
// EndpointZoneID determines zoneID for endpoint from map[zoneID]zoneName by taking longest suffix zoneName match in endpoint DNSName
// returns empty string if no match found
func (f *filter) EndpointZoneID(endpoint *endpoint.Endpoint, zones map[string]string) (zoneID string) {
var matchZoneID, matchZoneName string
for zoneID, zoneName := range zones {
if strings.HasSuffix(endpoint.DNSName, zoneName) && len(zoneName) > len(matchZoneName) {
matchZoneName = zoneName
matchZoneID = zoneID
}
}
return matchZoneID
}
// inMemoryRecord - record stored in memory
// Type - type of string
// Name - DNS name assigned to the record
// Target - target of the record
// Payload - string - additional information stored
type inMemoryRecord struct {
Type string
Payload string
Name string
Target string
}
type zone map[string][]*inMemoryRecord
type inMemoryChange struct {
Create []*inMemoryRecord
UpdateNew []*inMemoryRecord
UpdateOld []*inMemoryRecord
Delete []*inMemoryRecord
}
type inMemoryClient struct {
zones map[string]zone
}
func newInMemoryClient() *inMemoryClient {
return &inMemoryClient{map[string]zone{}}
}
func (c *inMemoryClient) Records(zone string) ([]*inMemoryRecord, error) {
if _, ok := c.zones[zone]; !ok {
return nil, ErrZoneNotFound
}
records := []*inMemoryRecord{}
for _, rec := range c.zones[zone] {
records = append(records, rec...)
}
return records, nil
}
func (c *inMemoryClient) Zones() map[string]string {
zones := map[string]string{}
for zone := range c.zones {
zones[zone] = zone
}
return zones
}
func (c *inMemoryClient) CreateZone(zone string) error {
if _, ok := c.zones[zone]; ok {
return ErrZoneAlreadyExists
}
c.zones[zone] = map[string][]*inMemoryRecord{}
return nil
}
func (c *inMemoryClient) ApplyChanges(zoneID string, changes *inMemoryChange) error {
if err := c.validateChangeBatch(zoneID, changes); err != nil {
return err
}
for _, newEndpoint := range changes.Create {
if _, ok := c.zones[zoneID][newEndpoint.Name]; !ok {
c.zones[zoneID][newEndpoint.Name] = make([]*inMemoryRecord, 0)
}
c.zones[zoneID][newEndpoint.Name] = append(c.zones[zoneID][newEndpoint.Name], newEndpoint)
}
for _, updateEndpoint := range changes.UpdateNew { for _, updateEndpoint := range changes.UpdateNew {
for _, curEndpoint := range changes.UpdateOld { for _, rec := range c.zones[zoneID][updateEndpoint.Name] {
if curEndpoint.DNSName == updateEndpoint.DNSName && curEndpoint.RecordType == updateEndpoint.RecordType { if rec.Type == updateEndpoint.Type {
for _, recordToUpdate := range im.zones[zone][updateEndpoint.DNSName] { rec.Target = updateEndpoint.Target
if recordToUpdate.Target == curEndpoint.Target { break
recordToUpdate.Target = updateEndpoint.Target
}
}
} }
} }
} }
for _, deleteEndpoint := range changes.Delete { for _, deleteEndpoint := range changes.Delete {
newRecordSet := make([]*InMemoryRecord, 0) newSet := make([]*inMemoryRecord, 0)
for _, record := range im.zones[zone][deleteEndpoint.DNSName] { for _, rec := range c.zones[zoneID][deleteEndpoint.Name] {
if record.Type != suitableType(deleteEndpoint) { if rec.Type != deleteEndpoint.Type {
newRecordSet = append(newRecordSet, record) newSet = append(newSet, rec)
} }
} }
im.zones[zone][deleteEndpoint.DNSName] = newRecordSet c.zones[zoneID][deleteEndpoint.Name] = newSet
} }
return nil return nil
} }
func (c *inMemoryClient) updateMesh(mesh map[string]map[string]bool, endpoint *inMemoryRecord) error {
if _, exists := mesh[endpoint.Name]; exists {
if mesh[endpoint.Name][endpoint.Type] {
return ErrInvalidBatchRequest
}
mesh[endpoint.Name][endpoint.Type] = true
return nil
}
mesh[endpoint.Name] = map[string]bool{endpoint.Type: true}
return nil
}
// validateChangeBatch validates that the changes passed to InMemory DNS provider is valid // validateChangeBatch validates that the changes passed to InMemory DNS provider is valid
func (im *InMemoryProvider) validateChangeBatch(zone string, changes *plan.Changes) error { func (c *inMemoryClient) validateChangeBatch(zone string, changes *inMemoryChange) error {
existing, ok := im.zones[zone] curZone, ok := c.zones[zone]
if !ok { if !ok {
return ErrZoneNotFound return ErrZoneNotFound
} }
mesh := map[string]map[string]bool{} mesh := map[string]map[string]bool{}
for _, newEndpoint := range changes.Create { for _, newEndpoint := range changes.Create {
if im.findByType(suitableType(newEndpoint), existing[newEndpoint.DNSName]) != nil { if c.findByType(newEndpoint.Type, curZone[newEndpoint.Name]) != nil {
return ErrRecordAlreadyExists return ErrRecordAlreadyExists
} }
if _, exists := mesh[newEndpoint.DNSName]; exists { if err := c.updateMesh(mesh, newEndpoint); err != nil {
if mesh[newEndpoint.DNSName][suitableType(newEndpoint)] { return err
return ErrInvalidBatchRequest
} }
mesh[newEndpoint.DNSName][suitableType(newEndpoint)] = true
continue
}
mesh[newEndpoint.DNSName] = map[string]bool{suitableType(newEndpoint): true}
} }
for _, updateEndpoint := range changes.UpdateNew { for _, updateEndpoint := range changes.UpdateNew {
if im.findByType(suitableType(updateEndpoint), existing[updateEndpoint.DNSName]) == nil { if c.findByType(updateEndpoint.Type, curZone[updateEndpoint.Name]) == nil {
return ErrRecordNotFound return ErrRecordNotFound
} }
if _, exists := mesh[updateEndpoint.DNSName]; exists { if err := c.updateMesh(mesh, updateEndpoint); err != nil {
if mesh[updateEndpoint.DNSName][suitableType(updateEndpoint)] { return err
return ErrInvalidBatchRequest
} }
mesh[updateEndpoint.DNSName][suitableType(updateEndpoint)] = true
continue
}
mesh[updateEndpoint.DNSName] = map[string]bool{suitableType(updateEndpoint): true}
} }
for _, updateOldEndpoint := range changes.UpdateOld { for _, updateOldEndpoint := range changes.UpdateOld {
if rec := im.findByType(suitableType(updateOldEndpoint), existing[updateOldEndpoint.DNSName]); rec == nil || rec.Target != updateOldEndpoint.Target { if rec := c.findByType(updateOldEndpoint.Type, curZone[updateOldEndpoint.Name]); rec == nil || rec.Target != updateOldEndpoint.Target {
return ErrRecordNotFound return ErrRecordNotFound
} }
} }
for _, deleteEndpoint := range changes.Delete { for _, deleteEndpoint := range changes.Delete {
if rec := im.findByType(suitableType(deleteEndpoint), existing[deleteEndpoint.DNSName]); rec == nil || rec.Target != deleteEndpoint.Target { if rec := c.findByType(deleteEndpoint.Type, curZone[deleteEndpoint.Name]); rec == nil || rec.Target != deleteEndpoint.Target {
return ErrRecordNotFound return ErrRecordNotFound
} }
if _, exists := mesh[deleteEndpoint.DNSName]; exists { if err := c.updateMesh(mesh, deleteEndpoint); err != nil {
if mesh[deleteEndpoint.DNSName][suitableType(deleteEndpoint)] { return err
return ErrInvalidBatchRequest
} }
mesh[deleteEndpoint.DNSName][suitableType(deleteEndpoint)] = true
continue
}
mesh[deleteEndpoint.DNSName] = map[string]bool{suitableType(deleteEndpoint): true}
} }
return nil return nil
} }
func (im *InMemoryProvider) findByType(recordType string, records []*InMemoryRecord) *InMemoryRecord { func (c *inMemoryClient) findByType(recordType string, records []*inMemoryRecord) *inMemoryRecord {
for _, record := range records { for _, record := range records {
if record.Type == recordType { if record.Type == recordType {
return record return record
@ -187,16 +323,3 @@ func (im *InMemoryProvider) findByType(recordType string, records []*InMemoryRec
} }
return nil return nil
} }
func (im *InMemoryProvider) endpoints(zone string) []*endpoint.Endpoint {
endpoints := make([]*endpoint.Endpoint, 0)
if zoneRecords, exists := im.zones[zone]; exists {
for _, recordsPerName := range zoneRecords {
for _, record := range recordsPerName {
record.Endpoint.RecordType = record.Type
endpoints = append(endpoints, record.Endpoint)
}
}
}
return endpoints
}

View File

@ -30,9 +30,8 @@ var (
) )
func TestInMemoryProvider(t *testing.T) { func TestInMemoryProvider(t *testing.T) {
t.Run("Records", testInMemoryRecords)
t.Run("endpoints", testInMemoryEndpoints)
t.Run("findByType", testInMemoryFindByType) t.Run("findByType", testInMemoryFindByType)
t.Run("Records", testInMemoryRecords)
t.Run("validateChangeBatch", testInMemoryValidateChangeBatch) t.Run("validateChangeBatch", testInMemoryValidateChangeBatch)
t.Run("ApplyChanges", testInMemoryApplyChanges) t.Run("ApplyChanges", testInMemoryApplyChanges)
t.Run("NewInMemoryProvider", testNewInMemoryProvider) t.Run("NewInMemoryProvider", testNewInMemoryProvider)
@ -43,8 +42,8 @@ func testInMemoryFindByType(t *testing.T) {
for _, ti := range []struct { for _, ti := range []struct {
title string title string
findType string findType string
records []*InMemoryRecord records []*inMemoryRecord
expected *InMemoryRecord expected *inMemoryRecord
expectedEmpty bool expectedEmpty bool
}{ }{
{ {
@ -64,7 +63,7 @@ func testInMemoryFindByType(t *testing.T) {
{ {
title: "one record, empty type", title: "one record, empty type",
findType: "", findType: "",
records: []*InMemoryRecord{ records: []*inMemoryRecord{
{ {
Type: "A", Type: "A",
}, },
@ -75,7 +74,7 @@ func testInMemoryFindByType(t *testing.T) {
{ {
title: "one record, wrong type", title: "one record, wrong type",
findType: "CNAME", findType: "CNAME",
records: []*InMemoryRecord{ records: []*inMemoryRecord{
{ {
Type: "A", Type: "A",
}, },
@ -86,19 +85,19 @@ func testInMemoryFindByType(t *testing.T) {
{ {
title: "one record, right type", title: "one record, right type",
findType: "A", findType: "A",
records: []*InMemoryRecord{ records: []*inMemoryRecord{
{ {
Type: "A", Type: "A",
}, },
}, },
expected: &InMemoryRecord{ expected: &inMemoryRecord{
Type: "A", Type: "A",
}, },
}, },
{ {
title: "multiple records, right type", title: "multiple records, right type",
findType: "A", findType: "A",
records: []*InMemoryRecord{ records: []*inMemoryRecord{
{ {
Type: "A", Type: "A",
}, },
@ -106,14 +105,14 @@ func testInMemoryFindByType(t *testing.T) {
Type: "TXT", Type: "TXT",
}, },
}, },
expected: &InMemoryRecord{ expected: &inMemoryRecord{
Type: "A", Type: "A",
}, },
}, },
} { } {
t.Run(ti.title, func(t *testing.T) { t.Run(ti.title, func(t *testing.T) {
im := NewInMemoryProvider() c := newInMemoryClient()
record := im.findByType(ti.findType, ti.records) record := c.findByType(ti.findType, ti.records)
if ti.expectedEmpty && record != nil { if ti.expectedEmpty && record != nil {
t.Errorf("should return nil") t.Errorf("should return nil")
} }
@ -127,10 +126,11 @@ func testInMemoryFindByType(t *testing.T) {
} }
} }
func testInMemoryEndpoints(t *testing.T) { func testInMemoryRecords(t *testing.T) {
for _, ti := range []struct { for _, ti := range []struct {
title string title string
zone string zone string
expectError bool
init map[string]zone init map[string]zone
expected []*endpoint.Endpoint expected []*endpoint.Endpoint
}{ }{
@ -138,102 +138,52 @@ func testInMemoryEndpoints(t *testing.T) {
title: "no records, no zone", title: "no records, no zone",
zone: "", zone: "",
init: map[string]zone{}, init: map[string]zone{},
expected: []*endpoint.Endpoint{}, expectError: false,
}, },
{ {
title: "no records, zone", title: "records, wrong zone",
zone: "central", zone: "net",
init: map[string]zone{},
expected: []*endpoint.Endpoint{},
},
{
title: "records, no zone",
zone: "",
init: map[string]zone{ init: map[string]zone{
"org": { "org": {},
"example.org": []*InMemoryRecord{ "com": {},
{},
}, },
"foo.org": []*InMemoryRecord{ expectError: false,
{},
},
},
"com": {
"example.com": []*InMemoryRecord{
{},
},
"foo.com": []*InMemoryRecord{
{},
},
},
},
expected: []*endpoint.Endpoint{},
},
{
title: "records, zone with no records",
zone: "",
init: map[string]zone{
"org": {
"example.org": []*InMemoryRecord{
{},
},
"foo.org": []*InMemoryRecord{
{},
},
},
"com": {
"example.com": []*InMemoryRecord{
{},
},
"foo.com": []*InMemoryRecord{
{},
},
},
},
expected: []*endpoint.Endpoint{},
}, },
{ {
title: "records, zone with records", title: "records, zone with records",
zone: "org", zone: "org",
init: map[string]zone{ init: map[string]zone{
"org": { "org": {
"example.org": []*InMemoryRecord{ "example.org": []*inMemoryRecord{
{ {
Endpoint: &endpoint.Endpoint{ Name: "example.org",
DNSName: "example.org",
Target: "8.8.8.8", Target: "8.8.8.8",
},
Type: "A", Type: "A",
}, },
{ {
Endpoint: &endpoint.Endpoint{ Name: "example.org",
DNSName: "example.org",
},
Type: "TXT", Type: "TXT",
}, },
}, },
"foo.org": []*InMemoryRecord{ "foo.org": []*inMemoryRecord{
{ {
Endpoint: &endpoint.Endpoint{ Name: "foo.org",
DNSName: "foo.org", Target: "4.4.4.4",
Target: "bar.org",
},
Type: "CNAME", Type: "CNAME",
}, },
}, },
}, },
"com": { "com": {
"example.com": []*InMemoryRecord{ "example.com": []*inMemoryRecord{
{ {
Endpoint: &endpoint.Endpoint{ Name: "example.com",
DNSName: "example.com",
Target: "4.4.4.4", Target: "4.4.4.4",
}, Type: "CNAME",
Type: "A",
}, },
}, },
}, },
}, },
expectError: false,
expected: []*endpoint.Endpoint{ expected: []*endpoint.Endpoint{
{ {
DNSName: "example.org", DNSName: "example.org",
@ -246,92 +196,20 @@ func testInMemoryEndpoints(t *testing.T) {
}, },
{ {
DNSName: "foo.org", DNSName: "foo.org",
Target: "bar.org", Target: "4.4.4.4",
RecordType: "CNAME", RecordType: "CNAME",
}, },
}, },
}, },
} { } {
t.Run(ti.title, func(t *testing.T) { t.Run(ti.title, func(t *testing.T) {
im := &InMemoryProvider{zones: ti.init} c := newInMemoryClient()
if !testutils.SameEndpoints(im.endpoints(ti.zone), ti.expected) { c.zones = ti.init
t.Errorf("endpoints returned wrong set")
}
})
}
}
func testInMemoryRecords(t *testing.T) {
for _, ti := range []struct {
title string
zone string
expectError bool
init map[string]zone
}{
{
title: "no records, no zone",
zone: "",
init: map[string]zone{},
expectError: true,
},
{
title: "records, wrong zone",
zone: "net",
init: map[string]zone{
"org": {},
"com": {},
},
expectError: true,
},
{
title: "records, zone with records",
zone: "org",
init: map[string]zone{
"org": {
"example.org": []*InMemoryRecord{
{
Endpoint: &endpoint.Endpoint{
DNSName: "example.org",
Target: "8.8.8.8",
},
Type: "A",
},
{
Endpoint: &endpoint.Endpoint{
DNSName: "example.org",
},
Type: "TXT",
},
},
"foo.org": []*InMemoryRecord{
{
Endpoint: &endpoint.Endpoint{
DNSName: "foo.org",
Target: "4.4.4.4",
},
Type: "CNAME",
},
},
},
"com": {
"example.com": []*InMemoryRecord{
{
Endpoint: &endpoint.Endpoint{
DNSName: "example.com",
Target: "4.4.4.4",
},
Type: "CNAME",
},
},
},
},
expectError: false,
},
} {
t.Run(ti.title, func(t *testing.T) {
im := NewInMemoryProvider() im := NewInMemoryProvider()
im.zones = ti.init im.client = c
records, err := im.Records(ti.zone) f := filter{domain: ti.zone}
im.filter = &f
records, err := im.Records("_")
if ti.expectError && records != nil { if ti.expectError && records != nil {
t.Errorf("wrong zone should not return records") t.Errorf("wrong zone should not return records")
} }
@ -341,7 +219,7 @@ func testInMemoryRecords(t *testing.T) {
if !ti.expectError && err != nil { if !ti.expectError && err != nil {
t.Errorf("unexpected error") t.Errorf("unexpected error")
} }
if !ti.expectError && !testutils.SameEndpoints(im.endpoints(ti.zone), records) { if !ti.expectError && !testutils.SameEndpoints(ti.expected, records) {
t.Errorf("endpoints returned wrong set") t.Errorf("endpoints returned wrong set")
} }
}) })
@ -351,47 +229,36 @@ func testInMemoryRecords(t *testing.T) {
func testInMemoryValidateChangeBatch(t *testing.T) { func testInMemoryValidateChangeBatch(t *testing.T) {
init := map[string]zone{ init := map[string]zone{
"org": { "org": {
"example.org": []*InMemoryRecord{ "example.org": []*inMemoryRecord{
{ {
Endpoint: &endpoint.Endpoint{ Name: "example.org",
DNSName: "example.org",
Target: "8.8.8.8", Target: "8.8.8.8",
},
Type: "A", Type: "A",
}, },
{ {
Endpoint: &endpoint.Endpoint{ Name: "example.org",
DNSName: "example.org",
},
Type: "TXT",
}, },
}, },
"foo.org": []*InMemoryRecord{ "foo.org": []*inMemoryRecord{
{ {
Endpoint: &endpoint.Endpoint{ Name: "foo.org",
DNSName: "foo.org",
Target: "bar.org", Target: "bar.org",
},
Type: "CNAME", Type: "CNAME",
}, },
}, },
"foo.bar.org": []*InMemoryRecord{ "foo.bar.org": []*inMemoryRecord{
{ {
Endpoint: &endpoint.Endpoint{ Name: "foo.bar.org",
DNSName: "foo.bar.org",
Target: "5.5.5.5", Target: "5.5.5.5",
},
Type: "A", Type: "A",
}, },
}, },
}, },
"com": { "com": {
"example.com": []*InMemoryRecord{ "example.com": []*inMemoryRecord{
{ {
Endpoint: &endpoint.Endpoint{ Name: "example.com",
DNSName: "example.com",
Target: "another-example.com", Target: "another-example.com",
},
Type: "CNAME", Type: "CNAME",
}, },
}, },
@ -657,10 +524,15 @@ func testInMemoryValidateChangeBatch(t *testing.T) {
}, },
} { } {
t.Run(ti.title, func(t *testing.T) { t.Run(ti.title, func(t *testing.T) {
im := &InMemoryProvider{ c := &inMemoryClient{}
zones: ti.init, c.zones = ti.init
ichanges := &inMemoryChange{
Create: convertToInMemoryRecord(ti.changes.Create),
UpdateNew: convertToInMemoryRecord(ti.changes.UpdateNew),
UpdateOld: convertToInMemoryRecord(ti.changes.UpdateOld),
Delete: convertToInMemoryRecord(ti.changes.Delete),
} }
err := im.validateChangeBatch(ti.zone, ti.changes) err := c.validateChangeBatch(ti.zone, ichanges)
if ti.expectError && err != ti.errorType { if ti.expectError && err != ti.errorType {
t.Errorf("returns wrong type of error: %v, expected: %v", err, ti.errorType) t.Errorf("returns wrong type of error: %v, expected: %v", err, ti.errorType)
} }
@ -718,39 +590,34 @@ func testInMemoryApplyChanges(t *testing.T) {
}, },
expectedZonesState: map[string]zone{ expectedZonesState: map[string]zone{
"org": { "org": {
"example.org": []*InMemoryRecord{ "example.org": []*inMemoryRecord{
{ {
Endpoint: &endpoint.Endpoint{
DNSName: "example.org", Name: "example.org",
Target: "8.8.8.8", Target: "8.8.8.8",
},
Type: "A", Type: "A",
}, },
{ {
Endpoint: &endpoint.Endpoint{
DNSName: "example.org", Name: "example.org",
},
Type: "TXT", Type: "TXT",
}, },
}, },
"foo.org": []*InMemoryRecord{ "foo.org": []*inMemoryRecord{
{ {
Endpoint: &endpoint.Endpoint{
DNSName: "foo.org", Name: "foo.org",
Target: "4.4.4.4", Target: "4.4.4.4",
},
Type: "CNAME", Type: "CNAME",
}, },
}, },
"foo.bar.org": []*InMemoryRecord{}, "foo.bar.org": []*inMemoryRecord{},
}, },
"com": { "com": {
"example.com": []*InMemoryRecord{ "example.com": []*inMemoryRecord{
{ {
Endpoint: &endpoint.Endpoint{ Name: "example.com",
DNSName: "example.com",
Target: "4.4.4.4", Target: "4.4.4.4",
},
Type: "CNAME", Type: "CNAME",
}, },
}, },
@ -789,49 +656,39 @@ func testInMemoryApplyChanges(t *testing.T) {
}, },
expectedZonesState: map[string]zone{ expectedZonesState: map[string]zone{
"org": { "org": {
"example.org": []*InMemoryRecord{ "example.org": []*inMemoryRecord{
{ {
Endpoint: &endpoint.Endpoint{ Name: "example.org",
DNSName: "example.org",
},
Type: "TXT", Type: "TXT",
}, },
}, },
"foo.org": []*InMemoryRecord{ "foo.org": []*inMemoryRecord{
{ {
Endpoint: &endpoint.Endpoint{ Name: "foo.org",
DNSName: "foo.org",
Target: "4.4.4.4", Target: "4.4.4.4",
},
Type: "CNAME", Type: "CNAME",
}, },
}, },
"foo.bar.org": []*InMemoryRecord{ "foo.bar.org": []*inMemoryRecord{
{ {
Endpoint: &endpoint.Endpoint{ Name: "foo.bar.org",
DNSName: "foo.bar.org",
Target: "4.8.8.4", Target: "4.8.8.4",
},
Type: "A", Type: "A",
}, },
}, },
"foo.bar.new.org": []*InMemoryRecord{ "foo.bar.new.org": []*inMemoryRecord{
{ {
Endpoint: &endpoint.Endpoint{ Name: "foo.bar.new.org",
DNSName: "foo.bar.new.org",
Target: "4.8.8.9", Target: "4.8.8.9",
},
Type: "A", Type: "A",
}, },
}, },
}, },
"com": { "com": {
"example.com": []*InMemoryRecord{ "example.com": []*inMemoryRecord{
{ {
Endpoint: &endpoint.Endpoint{ Name: "example.com",
DNSName: "example.com",
Target: "4.4.4.4", Target: "4.4.4.4",
},
Type: "CNAME", Type: "CNAME",
}, },
}, },
@ -842,56 +699,48 @@ func testInMemoryApplyChanges(t *testing.T) {
t.Run(ti.title, func(t *testing.T) { t.Run(ti.title, func(t *testing.T) {
init := map[string]zone{ init := map[string]zone{
"org": { "org": {
"example.org": []*InMemoryRecord{ "example.org": []*inMemoryRecord{
{ {
Endpoint: &endpoint.Endpoint{ Name: "example.org",
DNSName: "example.org",
Target: "8.8.8.8", Target: "8.8.8.8",
},
Type: "A", Type: "A",
}, },
{ {
Endpoint: &endpoint.Endpoint{ Name: "example.org",
DNSName: "example.org",
},
Type: "TXT", Type: "TXT",
}, },
}, },
"foo.org": []*InMemoryRecord{ "foo.org": []*inMemoryRecord{
{ {
Endpoint: &endpoint.Endpoint{ Name: "foo.org",
DNSName: "foo.org",
Target: "4.4.4.4", Target: "4.4.4.4",
},
Type: "CNAME", Type: "CNAME",
}, },
}, },
"foo.bar.org": []*InMemoryRecord{ "foo.bar.org": []*inMemoryRecord{
{ {
Endpoint: &endpoint.Endpoint{ Name: "foo.bar.org",
DNSName: "foo.bar.org",
Target: "5.5.5.5", Target: "5.5.5.5",
},
Type: "A", Type: "A",
}, },
}, },
}, },
"com": { "com": {
"example.com": []*InMemoryRecord{ "example.com": []*inMemoryRecord{
{ {
Endpoint: &endpoint.Endpoint{ Name: "example.com",
DNSName: "example.com",
Target: "4.4.4.4", Target: "4.4.4.4",
},
Type: "CNAME", Type: "CNAME",
}, },
}, },
}, },
} }
im := NewInMemoryProvider() im := NewInMemoryProvider()
im.zones = init c := &inMemoryClient{}
c.zones = init
im.client = c
err := im.ApplyChanges(ti.zone, ti.changes) err := im.ApplyChanges("_", ti.changes)
if ti.expectError && err == nil { if ti.expectError && err == nil {
t.Errorf("should return an error") t.Errorf("should return an error")
} }
@ -899,7 +748,7 @@ func testInMemoryApplyChanges(t *testing.T) {
t.Error(err) t.Error(err)
} }
if !ti.expectError { if !ti.expectError {
if !reflect.DeepEqual(im.zones, ti.expectedZonesState) { if !reflect.DeepEqual(c.zones, ti.expectedZonesState) {
t.Errorf("invalid update") t.Errorf("invalid update")
} }
} }
@ -909,7 +758,7 @@ func testInMemoryApplyChanges(t *testing.T) {
func testNewInMemoryProvider(t *testing.T) { func testNewInMemoryProvider(t *testing.T) {
cfg := NewInMemoryProvider() cfg := NewInMemoryProvider()
if cfg.zones == nil { if cfg.client == nil {
t.Error("nil map") t.Error("nil map")
} }
} }

View File

@ -46,25 +46,21 @@ func testNoopInit(t *testing.T) {
func testNoopRecords(t *testing.T) { func testNoopRecords(t *testing.T) {
p := provider.NewInMemoryProvider() p := provider.NewInMemoryProvider()
p.CreateZone("zone") p.CreateZone("org")
providerRecords := []*endpoint.Endpoint{ providerRecords := []*endpoint.Endpoint{
{ {
DNSName: "example.org", DNSName: "example.org",
Target: "example-lb.com", Target: "example-lb.com",
RecordType: "CNAME",
}, },
} }
p.ApplyChanges("zone", &plan.Changes{ p.ApplyChanges("_", &plan.Changes{
Create: providerRecords, Create: providerRecords,
}) })
r, _ := NewNoopRegistry(p) r, _ := NewNoopRegistry(p)
_, err := r.Records("wrong-zone")
if err == nil {
t.Error("Should fail for wrong zone: wrong-zone")
}
r, _ = NewNoopRegistry(p) eps, err := r.Records("_")
eps, err := r.Records("zone")
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@ -76,7 +72,7 @@ func testNoopRecords(t *testing.T) {
func testNoopApplyChanges(t *testing.T) { func testNoopApplyChanges(t *testing.T) {
// do some prep // do some prep
p := provider.NewInMemoryProvider() p := provider.NewInMemoryProvider()
p.CreateZone("zone") p.CreateZone("org")
providerRecords := []*endpoint.Endpoint{ providerRecords := []*endpoint.Endpoint{
{ {
DNSName: "example.org", DNSName: "example.org",
@ -96,20 +92,13 @@ func testNoopApplyChanges(t *testing.T) {
}, },
} }
p.ApplyChanges("zone", &plan.Changes{ p.ApplyChanges("_", &plan.Changes{
Create: providerRecords, Create: providerRecords,
}) })
// wrong zone
r, _ := NewNoopRegistry(p)
err := r.ApplyChanges("wrong-zone", &plan.Changes{})
if err != provider.ErrZoneNotFound {
t.Error("should return zone not found for apply changes on wrong zone")
}
// wrong changes // wrong changes
r, _ = NewNoopRegistry(p) r, _ := NewNoopRegistry(p)
err = r.ApplyChanges("zone", &plan.Changes{ err := r.ApplyChanges("_", &plan.Changes{
Create: []*endpoint.Endpoint{ Create: []*endpoint.Endpoint{
{ {
DNSName: "example.org", DNSName: "example.org",
@ -122,7 +111,7 @@ func testNoopApplyChanges(t *testing.T) {
} }
//correct changes //correct changes
err = r.ApplyChanges("zone", &plan.Changes{ err = r.ApplyChanges("_", &plan.Changes{
Create: []*endpoint.Endpoint{ Create: []*endpoint.Endpoint{
{ {
DNSName: "new-record.org", DNSName: "new-record.org",
@ -145,7 +134,7 @@ func testNoopApplyChanges(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
res, _ := p.Records("zone") res, _ := p.Records("_")
if !testutils.SameEndpoints(res, expectedUpdate) { if !testutils.SameEndpoints(res, expectedUpdate) {
t.Error("incorrectly updated dns provider") t.Error("incorrectly updated dns provider")
} }

View File

@ -26,7 +26,7 @@ import (
) )
const ( const (
testZone = "test-zone.example.com." testZone = "test-zone.example.org"
) )
func TestTXTRegistry(t *testing.T) { func TestTXTRegistry(t *testing.T) {
@ -60,11 +60,6 @@ func testTXTRegistryNew(t *testing.T) {
if _, ok := r.mapper.(prefixNameMapper); !ok { if _, ok := r.mapper.(prefixNameMapper); !ok {
t.Error("Incorrect type of prefix name mapper") t.Error("Incorrect type of prefix name mapper")
} }
rs, err := r.Records("random-zone")
if err == nil || rs != nil {
t.Error("incorrect zone should trigger error")
}
} }
func testTXTRegistryRecords(t *testing.T) { func testTXTRegistryRecords(t *testing.T) {
@ -75,7 +70,7 @@ func testTXTRegistryRecords(t *testing.T) {
func testTXTRegistryRecordsPrefixed(t *testing.T) { func testTXTRegistryRecordsPrefixed(t *testing.T) {
p := provider.NewInMemoryProvider() p := provider.NewInMemoryProvider()
p.CreateZone(testZone) p.CreateZone(testZone)
p.ApplyChanges(testZone, &plan.Changes{ p.ApplyChanges("_", &plan.Changes{
Create: []*endpoint.Endpoint{ Create: []*endpoint.Endpoint{
newEndpointWithOwner("foo.test-zone.example.org", "foo.loadbalancer.com", "CNAME", ""), newEndpointWithOwner("foo.test-zone.example.org", "foo.loadbalancer.com", "CNAME", ""),
newEndpointWithOwner("bar.test-zone.example.org", "my-domain.com", "CNAME", ""), newEndpointWithOwner("bar.test-zone.example.org", "my-domain.com", "CNAME", ""),
@ -295,13 +290,6 @@ func testTXTRegistryApplyChangesWithPrefix(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
changes = &plan.Changes{}
p.OnApplyChanges = func(c *plan.Changes) {}
err = r.ApplyChanges("new-zone", changes)
if err == nil {
t.Error("expected error")
}
} }
func testTXTRegistryApplyChangesNoPrefix(t *testing.T) { func testTXTRegistryApplyChangesNoPrefix(t *testing.T) {