TRANSIP: Improve diff2 implementation (#2228)

Co-authored-by: Tom Limoncelli <tlimoncelli@stackoverflow.com>
This commit is contained in:
Vincent Hagen 2023-03-26 18:51:45 +02:00 committed by GitHub
parent c34a1a6ed1
commit 9ffec690f5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 151 additions and 54 deletions

View file

@ -56,28 +56,13 @@ General instructions:
for _, change := range changes {
switch change.Type {
case diff2.REPORT:
corr = &models.Correction{Msg: change.MsgsJoined}
corr = change.CreateMessage()
case diff2.CREATE:
corr = &models.Correction{
Msg: change.MsgsJoined,
F: func() error {
return c.createRecord(FILL_IN)
},
}
corr = change.CreateCorrection(func() error { return c.createRecord(FILL_IN) })
case diff2.CHANGE:
corr = &models.Correction{
Msg: change.MsgsJoined,
F: func() error {
return c.modifyRecord(FILL_IN)
},
}
corr = change.CreateCorrection(func() error { return c.modifyRecord(FILL_IN) })
case diff2.DELETE:
corr = &models.Correction{
Msg: change.MsgsJoined,
F: func() error {
return c.deleteRecord(FILL_IN)
},
}
corr = change.CreateCorrection(func() error { return c.deleteRecord(FILL_IN) })
default:
panic("unhandled change.TYPE %s", change.Type)
}
@ -90,6 +75,33 @@ General instructions:
*/
// CreateCorrection creates a new Correction based on the given
// function and prefills it with the Msg of the current Change
func (c *Change) CreateCorrection(correctionFunction func() error) *models.Correction {
return &models.Correction{
F: correctionFunction,
Msg: c.MsgsJoined,
}
}
// CreateMessage creates a new correction with only the message.
// Used for diff2.Report corrections
func (c *Change) CreateMessage() *models.Correction {
return &models.Correction{
Msg: c.MsgsJoined,
}
}
// CreateCorrectionWithMessage creates a new Correction based on the
// given function and prefixes given function with the Msg of the
// current change
func (c *Change) CreateCorrectionWithMessage(msg string, correctionFunction func() error) *models.Correction {
return &models.Correction{
F: correctionFunction,
Msg: fmt.Sprintf("%s: %s", msg, c.MsgsJoined),
}
}
// ByRecordSet takes two lists of records (existing and desired) and
// returns instructions for turning existing into desired.
//

View file

@ -95,7 +95,6 @@ func (n *transipProvider) ListZones() ([]string, error) {
}
func (n *transipProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
curRecords, err := n.GetZoneRecords(dc.Name)
if err != nil {
return nil, err
@ -109,47 +108,137 @@ func (n *transipProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*mode
models.PostProcessRecords(curRecords)
var corrections []*models.Correction
if !diff2.EnableDiff2 || true { // Remove "|| true" when diff2 version arrives
if !diff2.EnableDiff2 {
corrections, err := n.getCorrectionsUsingOldDiff(dc, curRecords)
return corrections, err
}
differ := diff.New(dc)
_, create, del, modify, err := differ.IncrementalDiff(curRecords)
corrections, err := n.getCorrectionsUsingDiff2(dc, curRecords)
return corrections, err
}
func (n *transipProvider) getCorrectionsUsingDiff2(dc *models.DomainConfig, records models.Records) ([]*models.Correction, error) {
var corrections []*models.Correction
instructions, err := diff2.ByRecordSet(records, dc, nil)
if err != nil {
return nil, err
}
for _, change := range instructions {
switch change.Type {
case diff2.DELETE:
corrections = append(corrections, change.CreateCorrection(wrapChangeFunction(change.Old, func(rec domain.DNSEntry) error { return n.domains.RemoveDNSEntry(dc.Name, rec) })))
case diff2.CREATE:
corrections = append(corrections, change.CreateCorrection(wrapChangeFunction(change.New, func(rec domain.DNSEntry) error { return n.domains.AddDNSEntry(dc.Name, rec) })))
case diff2.CHANGE:
if canDirectApplyDNSEntries(change) {
corrections = append(corrections, change.CreateCorrection(wrapChangeFunction(change.Old, func(rec domain.DNSEntry) error { return n.domains.UpdateDNSEntry(dc.Name, rec) })))
} else {
deleteFunction := wrapChangeFunction(change.Old, func(rec domain.DNSEntry) error { return n.domains.RemoveDNSEntry(dc.Name, rec) })
createFunction := wrapChangeFunction(change.New, func(rec domain.DNSEntry) error { return n.domains.AddDNSEntry(dc.Name, rec) })
corrections = append(corrections, change.CreateCorrectionWithMessage("[1/2] delete", deleteFunction), change.CreateCorrectionWithMessage("[2/2] create", createFunction))
}
case diff2.REPORT:
corrections = append(corrections, change.CreateMessage())
}
}
return corrections, nil
}
func wrapChangeFunction(records models.Records, executer func(rec domain.DNSEntry) error) func() error {
return func() error {
for _, record := range records {
nativeRec, err := recordToNative(record)
if err != nil {
return err
}
if err := executer(nativeRec); err != nil {
return err
}
}
return nil
}
}
// canDirectApplyDNSEntries determines if a change can be done in a single API call or
// if we must remove the old records and re-create them. TransIP is unable to do certain
// changes in a single call. As we learn those situations, add them here.
func canDirectApplyDNSEntries(change diff2.Change) bool {
desired, existing := change.New, change.Old
if change.Type != diff2.CHANGE {
return true
}
if len(desired) != len(existing) {
return false
}
if len(desired) > 1 {
return false
}
for i := 0; i < len(desired); i++ {
if !canUpdateDNSEntry(desired[i], existing[i]) {
return false
}
}
return true
}
func (n *transipProvider) getCorrectionsUsingOldDiff(dc *models.DomainConfig, records models.Records) ([]*models.Correction, error) {
var corrections []*models.Correction
differ := diff.New(dc)
_, create, del, modify, err := differ.IncrementalDiff(records)
if err != nil {
return nil, err
}
for _, del := range del {
entry, err := recordToNative(del.Existing)
if err != nil {
return nil, err
}
for _, del := range del {
entry, err := recordToNative(del.Existing)
if err != nil {
return nil, err
}
corrections = append(corrections, &models.Correction{
Msg: del.String(),
F: func() error { return n.domains.RemoveDNSEntry(dc.Name, entry) },
})
}
corrections = append(corrections, &models.Correction{
Msg: del.String(),
F: func() error { return n.domains.RemoveDNSEntry(dc.Name, entry) },
})
for _, cre := range create {
entry, err := recordToNative(cre.Desired)
if err != nil {
return nil, err
}
for _, cre := range create {
entry, err := recordToNative(cre.Desired)
if err != nil {
return nil, err
}
corrections = append(corrections, &models.Correction{
Msg: cre.String(),
F: func() error { return n.domains.AddDNSEntry(dc.Name, entry) },
})
}
corrections = append(corrections, &models.Correction{
Msg: cre.String(),
F: func() error { return n.domains.AddDNSEntry(dc.Name, entry) },
})
for _, mod := range modify {
targetEntry, err := recordToNative(mod.Desired)
if err != nil {
return nil, err
}
for _, mod := range modify {
targetEntry, err := recordToNative(mod.Desired)
if err != nil {
return nil, err
}
// TransIP identifies records by (Label, TTL Type), we can only update it if only the contents has changed and there exists no records with the same name.
// In diff2 this is solved by diffing against the recordset for the old diff mechanism we always remove the record and re-add it.
// TransIP identifies records by (Label, TTL Type), we can only update it if only the contents
// has changed. Otherwise we delete the old record and create the new one
if canUpdateDNSEntry(mod.Desired, mod.Existing) {
corrections = append(corrections, &models.Correction{
Msg: mod.String(),
F: func() error { return n.domains.UpdateDNSEntry(dc.Name, targetEntry) },
})
} else {
oldEntry, err := recordToNative(mod.Existing)
if err != nil {
return nil, err
@ -165,14 +254,10 @@ func (n *transipProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*mode
F: func() error { return n.domains.AddDNSEntry(dc.Name, targetEntry) },
},
)
}
return corrections, nil
}
// Insert Future diff2 version here.
return corrections, nil
}