From ef081da1a6c8327ac1b27672f21a23c836b6ca7d Mon Sep 17 00:00:00 2001 From: Vincent Hagen Date: Mon, 4 Dec 2023 17:54:03 +0100 Subject: [PATCH] TRANSIP: Fixed integration tests: Edge cases and TXT records fixed (#2673) Co-authored-by: Tom Limoncelli --- integrationTest/integration_test.go | 18 ++++++++-- providers/transip/slashes.go | 11 ++++++ providers/transip/slashes_test.go | 25 +++++++++++++ providers/transip/transipProvider.go | 53 ++++++++++++++++++++-------- 4 files changed, 90 insertions(+), 17 deletions(-) create mode 100644 providers/transip/slashes.go create mode 100644 providers/transip/slashes_test.go diff --git a/integrationTest/integration_test.go b/integrationTest/integration_test.go index 821d1f09e..52398cb18 100644 --- a/integrationTest/integration_test.go +++ b/integrationTest/integration_test.go @@ -1035,11 +1035,17 @@ func makeTests(t *testing.T) []*TestGroup { // that. testgroup("CNAME", - tc("Record pointing to @", cname("foo", "**current-domain**")), + tc("Record pointing to @", + cname("foo", "**current-domain**"), + a("@", "1.2.3.4"), + ), ), testgroup("MX", - tc("Record pointing to @", mx("foo", 8, "**current-domain**")), + tc("Record pointing to @", + mx("foo", 8, "**current-domain**"), + a("@", "1.2.3.4"), + ), tc("Null MX", mx("@", 0, ".")), // RFC 7505 ), @@ -1246,6 +1252,7 @@ func makeTests(t *testing.T) []*TestGroup { "NAMEDOTCOM", // Their API is so damn slow. We'll add it back as needed. "NS1", // Free acct only allows 50 records, therefore we skip //"ROUTE53", // Batches up changes in pages. + "TRANSIP", // Doesn't page. Works fine. Due to the slow API we skip. ), tc("99 records", manyA("rec%04d", "1.2.3.4", 99)...), tc("100 records", manyA("rec%04d", "1.2.3.4", 100)...), @@ -1515,10 +1522,15 @@ func makeTests(t *testing.T) []*TestGroup { // them here. If you are writing a new provider, I have some good // news: These don't apply to you! - testgroup("ALIAS", + testgroup("ALIAS on apex", requires(providers.CanUseAlias), tc("ALIAS at root", alias("@", "foo.com.")), tc("change it", alias("@", "foo2.com.")), + ), + + testgroup("ALIAS on subdomain", + requires(providers.CanUseAlias), + not("TRANSIP"), // TransIP does support ALIAS records, but only for apex records (@) tc("ALIAS at subdomain", alias("test", "foo.com.")), tc("change it", alias("test", "foo2.com.")), ), diff --git a/providers/transip/slashes.go b/providers/transip/slashes.go new file mode 100644 index 000000000..1f322da32 --- /dev/null +++ b/providers/transip/slashes.go @@ -0,0 +1,11 @@ +package transip + +import ( + "regexp" +) + +var removeSlashesRegexp = regexp.MustCompile(`(?:\\(\\)+)|(?:\\)`) + +func removeSlashes(s string) string { + return removeSlashesRegexp.ReplaceAllString(s, "$1") +} diff --git a/providers/transip/slashes_test.go b/providers/transip/slashes_test.go new file mode 100644 index 000000000..e17778032 --- /dev/null +++ b/providers/transip/slashes_test.go @@ -0,0 +1,25 @@ +package transip + +import ( + "testing" +) + +func TestRemoveSlashes(t *testing.T) { + + data := [][]string{ + { + `quote"d`, `quote"d`, + `quote\"d`, `quote"d`, + `quote\\"d`, `quote\"d`, + `m\o\\r\\\\e`, `mo\r\\e`, + }, + } + + for _, testCase := range data { + result := removeSlashes(testCase[0]) + if result != testCase[1] { + t.Fatalf(`Failed on "%s". Expected "%s"; got "%s".`, testCase[0], testCase[1], result) + } + } + +} diff --git a/providers/transip/transipProvider.go b/providers/transip/transipProvider.go index aadb70782..f273c9241 100644 --- a/providers/transip/transipProvider.go +++ b/providers/transip/transipProvider.go @@ -156,22 +156,9 @@ func (n *transipProvider) getCorrectionsUsingDiff2(dc *models.DomainConfig, reco ) corrections = append(corrections, correction) } else { - - oldEntries, err := recordsToNative(change.Old, true) - if err != nil { - return corrections, err - } - newEntries, err := recordsToNative(change.New, false) - if err != nil { - return corrections, err - } - - deleteCorrection := wrapChangeFunction(oldEntries, func(rec domain.DNSEntry) error { return n.domains.RemoveDNSEntry(dc.Name, rec) }) - createCorrection := wrapChangeFunction(newEntries, func(rec domain.DNSEntry) error { return n.domains.AddDNSEntry(dc.Name, rec) }) corrections = append( corrections, - change.CreateCorrectionWithMessage("[1/2] delete", deleteCorrection), - change.CreateCorrectionWithMessage("[2/2] create", createCorrection), + n.recreateRecordSet(dc, change)..., ) } case diff2.REPORT: @@ -183,6 +170,42 @@ func (n *transipProvider) getCorrectionsUsingDiff2(dc *models.DomainConfig, reco return corrections, nil } +func (n *transipProvider) recreateRecordSet(dc *models.DomainConfig, change diff2.Change) []*models.Correction { + var corrections []*models.Correction + + for _, rec := range change.Old { + if existsInRecords(rec, change.New) { + continue + } + + nativeRec, _ := recordToNative(rec, true) + createCorrection := change.CreateCorrectionWithMessage("[2/2] delete", func() error { return n.domains.RemoveDNSEntry(dc.Name, nativeRec) }) + corrections = append(corrections, createCorrection) + } + + for _, rec := range change.New { + if existsInRecords(rec, change.Old) { + continue + } + + nativeRec, _ := recordToNative(rec, false) + createCorrection := change.CreateCorrectionWithMessage("[1/2] create", func() error { return n.domains.AddDNSEntry(dc.Name, nativeRec) }) + corrections = append(corrections, createCorrection) + } + + return corrections +} + +func existsInRecords(rec *models.RecordConfig, set models.Records) bool { + for _, existing := range set { + if rec.ToComparableNoTTL() == existing.ToComparableNoTTL() && rec.TTL == existing.TTL { + return true + } + } + + return false +} + func recordsToNative(records models.Records, useOriginal bool) ([]domain.DNSEntry, error) { entries := make([]domain.DNSEntry, len(records)) @@ -324,6 +347,8 @@ func getTargetRecordContent(rc *models.RecordConfig) string { return fmt.Sprintf("%d %d %d %s", rc.DsKeyTag, rc.DsAlgorithm, rc.DsDigestType, rc.DsDigest) case "SRV": return fmt.Sprintf("%d %d %d %s", rc.SrvPriority, rc.SrvWeight, rc.SrvPort, rc.GetTargetField()) + case "TXT": + return removeSlashes(models.StripQuotes(rc.GetTargetCombined())) default: return models.StripQuotes(rc.GetTargetCombined()) }