Correctly group R53_ALIAS records during IncrementalDiff. (#399)

Previously, unnecessary corrections were possible if both an R53_ALIAS
pointing to an A record and to an AAAA record existed for the same label,
and map iteration over existing and desired found them in different orders.
(This is a common configuration for IPv6-enabled records.)

This commit:
 * mirrors key logic in the R53 provider
 * centralizes logic around keys in the models package
 * adds tests
This commit is contained in:
Ed Bardsley 2018-09-04 07:55:27 -07:00 committed by Craig Peterson
parent 50c126c2d4
commit 61c92c9215
4 changed files with 51 additions and 38 deletions

View file

@ -246,7 +246,15 @@ type RecordKey struct {
// Key converts a RecordConfig into a RecordKey.
func (rc *RecordConfig) Key() RecordKey {
return RecordKey{rc.Name, rc.Type}
t := rc.Type
if rc.R53Alias != nil {
if v, ok := rc.R53Alias["type"]; ok {
// Route53 aliases append their alias type, so that records for the same
// label with different alias types are considered separate.
t = fmt.Sprintf("%s_%s", t, v)
}
}
return RecordKey{rc.Name, t}
}
// Records is a list of *RecordConfig.

33
models/record_test.go Normal file
View file

@ -0,0 +1,33 @@
package models
import "testing"
func TestKey(t *testing.T) {
var tests = []struct {
rc RecordConfig
expected RecordKey
}{
{
RecordConfig{Type: "A", Name: "@"},
RecordKey{Type: "A", Name: "@"},
},
{
RecordConfig{Type: "R53_ALIAS", Name: "@"},
RecordKey{Type: "R53_ALIAS", Name: "@"},
},
{
RecordConfig{Type: "R53_ALIAS", Name: "@", R53Alias: map[string]string{"foo": "bar"}},
RecordKey{Type: "R53_ALIAS", Name: "@"},
},
{
RecordConfig{Type: "R53_ALIAS", Name: "@", R53Alias: map[string]string{"type": "AAAA"}},
RecordKey{Type: "R53_ALIAS_AAAA", Name: "@"},
},
}
for i, test := range tests {
actual := test.rc.Key()
if test.expected != actual {
t.Errorf("%d: Expected %s, got %s", i, test.expected, actual)
}
}
}

View file

@ -68,16 +68,14 @@ func (d *differ) IncrementalDiff(existing []*models.RecordConfig) (unchanged, cr
desired := d.dc.Records
// sort existing and desired by name
type key struct {
name, rType string
}
existingByNameAndType := map[key][]*models.RecordConfig{}
desiredByNameAndType := map[key][]*models.RecordConfig{}
existingByNameAndType := map[models.RecordKey][]*models.RecordConfig{}
desiredByNameAndType := map[models.RecordKey][]*models.RecordConfig{}
for _, e := range existing {
if d.matchIgnored(e.GetLabel()) {
log.Printf("Ignoring record %s %s due to IGNORE", e.GetLabel(), e.Type)
} else {
k := key{e.GetLabelFQDN(), e.Type}
k := e.Key()
existingByNameAndType[k] = append(existingByNameAndType[k], e)
}
}
@ -85,7 +83,7 @@ func (d *differ) IncrementalDiff(existing []*models.RecordConfig) (unchanged, cr
if d.matchIgnored(dr.GetLabel()) {
panic(fmt.Sprintf("Trying to update/add IGNOREd record: %s %s", dr.GetLabel(), dr.Type))
} else {
k := key{dr.GetLabelFQDN(), dr.Type}
k := dr.Key()
desiredByNameAndType[k] = append(desiredByNameAndType[k], dr)
}
}
@ -93,7 +91,7 @@ func (d *differ) IncrementalDiff(existing []*models.RecordConfig) (unchanged, cr
if d.dc.KeepUnknown {
for k := range existingByNameAndType {
if _, ok := desiredByNameAndType[k]; !ok {
log.Printf("Ignoring record set %s %s due to NO_PURGE", k.rType, k.name)
log.Printf("Ignoring record set %s %s due to NO_PURGE", k.Type, k.Name)
delete(existingByNameAndType, k)
}
}

View file

@ -102,21 +102,6 @@ func (r *route53Provider) getZones() error {
return nil
}
// map key for grouping records
type key struct {
Name, Type string
}
func getKey(r *models.RecordConfig) key {
var recordType = r.Type
if r.R53Alias != nil {
recordType = fmt.Sprintf("%s_%s", recordType, r.R53Alias["type"])
}
return key{r.GetLabelFQDN(), recordType}
}
type errNoExist struct {
domain string
}
@ -175,29 +160,18 @@ func (r *route53Provider) GetDomainCorrections(dc *models.DomainConfig) ([]*mode
// diff
differ := diff.New(dc, getAliasMap)
_, create, delete, modify := differ.IncrementalDiff(existingRecords)
namesToUpdate := map[key][]string{}
for _, c := range create {
namesToUpdate[getKey(c.Desired)] = append(namesToUpdate[getKey(c.Desired)], c.String())
}
for _, d := range delete {
namesToUpdate[getKey(d.Existing)] = append(namesToUpdate[getKey(d.Existing)], d.String())
}
for _, m := range modify {
namesToUpdate[getKey(m.Desired)] = append(namesToUpdate[getKey(m.Desired)], m.String())
}
namesToUpdate := differ.ChangedGroups(existingRecords)
if len(namesToUpdate) == 0 {
return nil, nil
}
updates := map[key][]*models.RecordConfig{}
updates := map[models.RecordKey][]*models.RecordConfig{}
// for each name we need to update, collect relevant records from dc
for k := range namesToUpdate {
updates[k] = nil
for _, rc := range dc.Records {
if getKey(rc) == k {
if rc.Key() == k {
updates[k] = append(updates[k], rc)
}
}