From 80da9857f9ee18cb68dd1a389c82b0f15d044ae9 Mon Sep 17 00:00:00 2001 From: Tom Limoncelli Date: Sun, 1 Oct 2023 10:00:39 -0400 Subject: [PATCH 1/7] CHORE: Remove diff1 from codebase --- commands/commands.go | 13 +- commands/previewPush.go | 9 +- integrationTest/integration_test.go | 101 +-- models/dns.go | 30 +- models/domain.go | 2 - pkg/diff/diff.go | 668 ++++++++-------- pkg/diff/diff2compat.go | 8 +- pkg/diff/diff_test.go | 735 +++++++++--------- pkg/diff2/flag.go | 3 - pkg/js/helpers.js | 19 - pkg/js/parse_tests/005-ignored-records.json | 82 +- .../parse_tests/023-ignored-glob-records.json | 8 +- pkg/normalize/validate.go | 8 +- .../akamaiedgedns/akamaiEdgeDnsProvider.go | 9 +- providers/autodns/autoDnsProvider.go | 98 --- providers/axfrddns/axfrddnsProvider.go | 104 --- providers/azuredns/azureDnsProvider.go | 141 +--- providers/bind/bindProvider.go | 160 ++-- providers/cloudflare/cloudflareProvider.go | 101 --- providers/cloudns/cloudnsProvider.go | 9 +- providers/cscglobal/dns.go | 10 +- providers/desec/desecProvider.go | 10 +- .../digitalocean/digitaloceanProvider.go | 9 +- providers/dnsimple/dnsimpleProvider.go | 9 +- providers/dnsmadeeasy/dnsMadeEasyProvider.go | 9 +- providers/domainnameshop/dns.go | 9 +- providers/exoscale/exoscaleProvider.go | 9 +- providers/gandiv5/gandi_v5Provider.go | 114 +-- providers/gcloud/gcloudProvider.go | 9 +- providers/gcore/gcoreProvider.go | 152 +--- providers/hedns/hednsProvider.go | 42 - providers/hetzner/hetznerProvider.go | 12 +- providers/hexonet/records.go | 19 +- providers/hostingde/hostingdeProvider.go | 8 +- providers/inwx/inwxProvider.go | 9 +- providers/linode/linodeProvider.go | 13 +- providers/loopia/loopiaProvider.go | 11 +- providers/luadns/luadnsProvider.go | 29 +- providers/msdns/corrections.go | 55 +- providers/namecheap/namecheapProvider.go | 9 +- providers/namedotcom/records.go | 12 +- providers/netcup/netcupProvider.go | 12 +- providers/netlify/netlifyProvider.go | 12 +- providers/ns1/ns1Provider.go | 47 +- providers/oracle/oracleProvider.go | 9 +- providers/ovh/ovhProvider.go | 46 +- providers/packetframe/packetframeProvider.go | 12 +- providers/porkbun/porkbunProvider.go | 58 +- providers/powerdns/diff.go | 61 +- providers/powerdns/dns.go | 10 +- providers/route53/route53Provider.go | 193 ----- providers/rwth/dns.go | 9 +- providers/softlayer/softlayerProvider.go | 11 +- providers/transip/transipProvider.go | 76 +- providers/vultr/vultrProvider.go | 44 +- 55 files changed, 897 insertions(+), 2570 deletions(-) diff --git a/commands/commands.go b/commands/commands.go index ac858550bf..0155763b25 100644 --- a/commands/commands.go +++ b/commands/commands.go @@ -65,14 +65,17 @@ func Run(v string) int { Destination: &js.EnableFetch, }, &cli.BoolFlag{ - Name: "diff2", - Usage: "Enable replacement diff algorithm", - Destination: &diff2.EnableDiff2, - Value: true, + Name: "diff2", + Usage: "Obsolete flag. Will be removed in v5 or later", + Hidden: true, + Action: func(ctx *cli.Context, v bool) error { + obsoleteDiff2FlagUsed = true + return nil + }, }, &cli.BoolFlag{ Name: "disableordering", - Usage: "Disables the dns ordering part of the diff2 package", + Usage: "Disables update reordering", Destination: &diff2.DisableOrdering, }, &cli.BoolFlag{ diff --git a/commands/previewPush.go b/commands/previewPush.go index 38b6a3543b..fb5a400568 100644 --- a/commands/previewPush.go +++ b/commands/previewPush.go @@ -12,7 +12,6 @@ import ( "github.com/StackExchange/dnscontrol/v4/models" "github.com/StackExchange/dnscontrol/v4/pkg/bindserial" "github.com/StackExchange/dnscontrol/v4/pkg/credsfile" - "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/pkg/nameservers" "github.com/StackExchange/dnscontrol/v4/pkg/normalize" "github.com/StackExchange/dnscontrol/v4/pkg/notifications" @@ -129,6 +128,8 @@ func Push(args PushArgs) error { return run(args.PreviewArgs, true, args.Interactive, printer.DefaultPrinter, &args.Report) } +var obsoleteDiff2FlagUsed = false + // run is the main routine common to preview/push func run(args PreviewArgs, push bool, interactive bool, out printer.CLI, report *string) error { // TODO: make truly CLI independent. Perhaps return results on a channel as they occur @@ -136,10 +137,8 @@ func run(args PreviewArgs, push bool, interactive bool, out printer.CLI, report // This is a hack until we have the new printer replacement. printer.SkinnyReport = !args.Full - if diff2.EnableDiff2 { - printer.Println("INFO: Diff2 algorithm in use. Welcome to the future!") - } else { - printer.Println("WARNING: Diff1 algorithm in use. Please upgrade to diff2 (`dnscontrol --diff2=true preview`) as diff1 will go away after 2023-07-05. See https://github.com/StackExchange/dnscontrol/issues/2262") + if obsoleteDiff2FlagUsed { + printer.Println("WARNING: Please remove obsolete --diff2 flag. This will be an error in v5 or later. See https://github.com/StackExchange/dnscontrol/issues/2262") } cfg, err := GetDNSConfig(args.GetDNSConfigArgs) diff --git a/integrationTest/integration_test.go b/integrationTest/integration_test.go index a40288104a..4913bfb1f9 100644 --- a/integrationTest/integration_test.go +++ b/integrationTest/integration_test.go @@ -12,7 +12,6 @@ import ( "github.com/StackExchange/dnscontrol/v4/models" "github.com/StackExchange/dnscontrol/v4/pkg/credsfile" - "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/pkg/nameservers" "github.com/StackExchange/dnscontrol/v4/pkg/zonerecs" "github.com/StackExchange/dnscontrol/v4/providers" @@ -31,7 +30,6 @@ var enableCFWorkers = flag.Bool("cfworkers", true, "Set false to disable CF work func init() { testing.Init() - flag.BoolVar(&diff2.EnableDiff2, "diff2", false, "enable diff2") flag.Parse() } @@ -139,11 +137,6 @@ func getDomainConfigWithNameservers(t *testing.T, prv providers.DNSServiceProvid // error explaining why it is not. func testPermitted(t *testing.T, p string, f TestGroup) error { - // Does this test require "diff2"? - if f.diff2only && !diff2.EnableDiff2 { - return fmt.Errorf("test for diff2 only") - } - // not() and only() can't be mixed. if len(f.only) != 0 && len(f.not) != 0 { return fmt.Errorf("invalid filter: can't mix not() and only()") @@ -223,8 +216,6 @@ func makeChanges(t *testing.T, prv providers.DNSServiceProvider, dc *models.Doma // records (A or AAAA) dom.Records = append(dom.Records, a("ns."+domainName+".", "9.8.7.6")) } - dom.IgnoredNames = tst.IgnoredNames - dom.IgnoredTargets = tst.IgnoredTargets dom.Unmanaged = tst.Unmanaged dom.UnmanagedUnsafe = tst.UnmanagedUnsafe models.PostProcessRecords(dom.Records) @@ -443,14 +434,11 @@ type TestGroup struct { not []string trueflags []bool tests []*TestCase - diff2only bool } type TestCase struct { Desc string Records []*models.RecordConfig - IgnoredNames []*models.IgnoreName - IgnoredTargets []*models.IgnoreTarget Unmanaged []*models.UnmanagedConfig UnmanagedUnsafe bool // DISABLE_IGNORE_SAFETY_CHECK Changeless bool // set to true if any changes would be an error @@ -468,11 +456,6 @@ func (tc *TestCase) UnsafeIgnore() *TestCase { return tc } -func (tg *TestGroup) Diff2Only() *TestGroup { - tg.diff2only = true - return tg -} - func SetLabel(r *models.RecordConfig, label, domain string) { r.Name = label r.NameFQDN = dnsutil.AddOrigin(label, "**current-domain**") @@ -547,29 +530,27 @@ func ds(name string, keyTag uint16, algorithm, digestType uint8, digest string) } func ignoreName(labelSpec string) *models.RecordConfig { - r := &models.RecordConfig{ - Type: "IGNORE_NAME", - Metadata: map[string]string{}, - } - // diff1 - SetLabel(r, labelSpec, "**current-domain**") - // diff2 - r.Metadata["ignore_LabelPattern"] = labelSpec - return r + return ignore(labelSpec, "*", "*") + // r := &models.RecordConfig{ + // Type: "IGNORE_NAME", + // Metadata: map[string]string{}, + // } + // SetLabel(r, labelSpec, "**current-domain**") + + // r.Metadata["ignore_LabelPattern"] = labelSpec + // return r } func ignoreTarget(targetSpec string, typeSpec string) *models.RecordConfig { - r := &models.RecordConfig{ - Type: "IGNORE_TARGET", - Metadata: map[string]string{}, - } - // diff1 - r.SetTarget(typeSpec) - SetLabel(r, targetSpec, "**current-domain**") - // diff2 - r.Metadata["ignore_RTypePattern"] = typeSpec - r.Metadata["ignore_TargetPattern"] = typeSpec - return r + return ignore("*", "*", targetSpec) + // r := &models.RecordConfig{ + // Type: "IGNORE_TARGET", + // Metadata: map[string]string{}, + // } + + // r.Metadata["ignore_RTypePattern"] = typeSpec + // r.Metadata["ignore_TargetPattern"] = typeSpec + // return r } func ignore(labelSpec string, typeSpec string, targetSpec string) *models.RecordConfig { @@ -577,9 +558,7 @@ func ignore(labelSpec string, typeSpec string, targetSpec string) *models.Record Type: "IGNORE", Metadata: map[string]string{}, } - if r.Metadata == nil { - r.Metadata = map[string]string{} - } + r.Metadata["ignore_LabelPattern"] = labelSpec r.Metadata["ignore_RTypePattern"] = typeSpec r.Metadata["ignore_TargetPattern"] = targetSpec @@ -717,50 +696,24 @@ func testgroup(desc string, items ...interface{}) *TestGroup { func tc(desc string, recs ...*models.RecordConfig) *TestCase { var records []*models.RecordConfig - var ignoredNames []*models.IgnoreName - var ignoredTargets []*models.IgnoreTarget var unmanagedItems []*models.UnmanagedConfig for _, r := range recs { switch r.Type { case "IGNORE": - // diff1: - ignoredNames = append(ignoredNames, &models.IgnoreName{ - Pattern: r.Metadata["ignore_LabelPattern"], - Types: r.Metadata["ignore_RTypePattern"], - }) - // diff2: unmanagedItems = append(unmanagedItems, &models.UnmanagedConfig{ LabelPattern: r.Metadata["ignore_LabelPattern"], RTypePattern: r.Metadata["ignore_RTypePattern"], TargetPattern: r.Metadata["ignore_TargetPattern"], }) continue - case "IGNORE_NAME": - ignoredNames = append(ignoredNames, &models.IgnoreName{Pattern: r.GetLabel(), Types: r.GetTargetField()}) - unmanagedItems = append(unmanagedItems, &models.UnmanagedConfig{ - LabelPattern: r.GetLabel(), - RTypePattern: r.GetTargetField(), - }) - continue - case "IGNORE_TARGET": - ignoredTargets = append(ignoredTargets, &models.IgnoreTarget{ - Pattern: r.GetLabel(), - Type: r.GetTargetField(), - }) - unmanagedItems = append(unmanagedItems, &models.UnmanagedConfig{ - RTypePattern: r.GetTargetField(), - TargetPattern: r.GetLabel(), - }) default: records = append(records, r) } } return &TestCase{ - Desc: desc, - Records: records, - IgnoredNames: ignoredNames, - IgnoredTargets: ignoredTargets, - Unmanaged: unmanagedItems, + Desc: desc, + Records: records, + Unmanaged: unmanagedItems, } } @@ -1915,7 +1868,7 @@ func makeTests(t *testing.T) []*TestGroup { tc("ignore manytypes", ignore("", "A,TXT", ""), ).ExpectNoChanges(), - ).Diff2Only(), + ), testgroup("IGNORE apex", tc("Create some records", @@ -1936,7 +1889,7 @@ func makeTests(t *testing.T) []*TestGroup { tc("ignore manytypes", ignore("", "A,TXT", ""), ).ExpectNoChanges().UnsafeIgnore(), - ).Diff2Only(), + ), // Legacy IGNORE_NAME and IGNORE_TARGET tests. @@ -1969,7 +1922,7 @@ func makeTests(t *testing.T) []*TestGroup { ignoreName("*.foo"), a("bar", "1.2.3.4"), ), - ).Diff2Only(), + ), testgroup("IGNORE_NAME apex", tc("Create some records", @@ -1990,7 +1943,7 @@ func makeTests(t *testing.T) []*TestGroup { a("bar", "2.4.6.8"), a("added", "4.6.8.9"), ).UnsafeIgnore(), - ).Diff2Only(), + ), testgroup("IGNORE_TARGET function CNAME", tc("Create some records", @@ -2059,7 +2012,7 @@ func makeTests(t *testing.T) []*TestGroup { tc("Add a new record - ignoring test.foo.com.", ignoreTarget("**.acm-validations.aws.", "CNAME"), ).ExpectNoChanges(), - ).Diff2Only(), + ), testgroup("structured TXT", only("OVH"), diff --git a/models/dns.go b/models/dns.go index a3dd1cf3a2..7b73eb895c 100644 --- a/models/dns.go +++ b/models/dns.go @@ -119,18 +119,18 @@ func (config *DNSConfig) DomainContainingFQDN(fqdn string) *DomainConfig { return d } -// IgnoreName describes an IGNORE_NAME rule. -type IgnoreName struct { - Pattern string `json:"pattern"` // Glob pattern. - Types string `json:"types"` // All caps rtype names, comma separated. -} - -// IgnoreTarget describes an IGNORE_TARGET rule. -type IgnoreTarget struct { - Pattern string `json:"pattern"` // Glob pattern. - Type string `json:"type"` // All caps rtype name. -} - -func (i *IgnoreTarget) String() string { - return i.Pattern -} +//// IgnoreName describes an IGNORE_NAME rule. +//type IgnoreName struct { +// Pattern string `json:"pattern"` // Glob pattern. +// Types string `json:"types"` // All caps rtype names, comma separated. +//} +// +//// IgnoreTarget describes an IGNORE_TARGET rule. +//type IgnoreTarget struct { +// Pattern string `json:"pattern"` // Glob pattern. +// Type string `json:"type"` // All caps rtype name. +//} +// +//func (i *IgnoreTarget) String() string { +// return i.Pattern +//} diff --git a/models/domain.go b/models/domain.go index b7640745d6..c25d9f7514 100644 --- a/models/domain.go +++ b/models/domain.go @@ -30,8 +30,6 @@ type DomainConfig struct { EnsureAbsent Records `json:"recordsabsent,omitempty"` // ENSURE_ABSENT KeepUnknown bool `json:"keepunknown,omitempty"` // NO_PURGE - IgnoredNames []*IgnoreName `json:"ignored_names,omitempty"` - IgnoredTargets []*IgnoreTarget `json:"ignored_targets,omitempty"` Unmanaged []*UnmanagedConfig `json:"unmanaged,omitempty"` // IGNORE() UnmanagedUnsafe bool `json:"unmanaged_disable_safety_check,omitempty"` // DISABLE_IGNORE_SAFETY_CHECK diff --git a/pkg/diff/diff.go b/pkg/diff/diff.go index b946b9acfb..4c27dbd9ce 100644 --- a/pkg/diff/diff.go +++ b/pkg/diff/diff.go @@ -1,14 +1,8 @@ package diff import ( - "fmt" - "regexp" - "sort" - "github.com/StackExchange/dnscontrol/v4/models" - "github.com/StackExchange/dnscontrol/v4/pkg/printer" "github.com/fatih/color" - "github.com/gobwas/glob" ) // Correlation stores a difference between two records. @@ -30,33 +24,33 @@ type Differ interface { ChangedGroups(existing []*models.RecordConfig) (map[models.RecordKey][]string, error) } -// New is a constructor for a Differ. -func New(dc *models.DomainConfig, extraValues ...func(*models.RecordConfig) map[string]string) Differ { - return &differ{ - dc: dc, - extraValues: extraValues, +// // New is a constructor for a Differ. +// func New(dc *models.DomainConfig, extraValues ...func(*models.RecordConfig) map[string]string) Differ { +// return &differ{ +// dc: dc, +// extraValues: extraValues, - // compile IGNORE_NAME glob patterns - compiledIgnoredNames: compileIgnoredNames(dc.IgnoredNames), +// // compile IGNORE_NAME glob patterns +// compiledIgnoredNames: compileIgnoredNames(dc.IgnoredNames), - // compile IGNORE_TARGET glob patterns - compiledIgnoredTargets: compileIgnoredTargets(dc.IgnoredTargets), - } -} +// // compile IGNORE_TARGET glob patterns +// compiledIgnoredTargets: compileIgnoredTargets(dc.IgnoredTargets), +// } +// } -// An ignoredName must match both the name glob and one of the recordTypes in rTypes. If rTypes is empty, any -// record type will match. -type ignoredName struct { - nameGlob glob.Glob - rTypes []string -} +// // An ignoredName must match both the name glob and one of the recordTypes in rTypes. If rTypes is empty, any +// // record type will match. +// type ignoredName struct { +// nameGlob glob.Glob +// rTypes []string +// } type differ struct { - dc *models.DomainConfig - extraValues []func(*models.RecordConfig) map[string]string + // dc *models.DomainConfig + // extraValues []func(*models.RecordConfig) map[string]string - compiledIgnoredNames []ignoredName - compiledIgnoredTargets []glob.Glob + // compiledIgnoredNames []ignoredName + // compiledIgnoredTargets []glob.Glob } // get normalized content for record. target, ttl, mxprio, and specified metadata @@ -64,196 +58,196 @@ func (d *differ) content(r *models.RecordConfig) string { // get the extra values maps to add to the comparison. var allMaps []map[string]string - for _, f := range d.extraValues { - valueMap := f(r) - allMaps = append(allMaps, valueMap) - } + // for _, f := range d.extraValues { + // valueMap := f(r) + // allMaps = append(allMaps, valueMap) + // } return r.ToDiffable(allMaps...) } -func apexException(rec *models.RecordConfig) bool { - // Providers often add NS and SOA records at the apex. These - // should not be included in certain checks. - return (rec.Type == "NS" || rec.Type == "SOA") && rec.GetLabel() == "@" -} - -func ignoreNameException(rec *models.RecordConfig) bool { - // People wanted it to be possible to disable this safety check. - // Ok, here it is. You now have two risks: - // 1. Two owners (DNSControl and some other entity) toggling a record between two settings. - // 2. The other owner wiping all records at this label, which won't be noticed until the next time dnscontrol is run. - //fmt.Printf("********** DEBUG IGNORE %v %v %q\n", rec.GetLabel(), rec.Type, rec.Metadata["ignore_name_disable_safety_check"]) - // See https://github.com/StackExchange/dnscontrol/issues/1106 - _, ok := rec.Metadata["ignore_name_disable_safety_check"] - return ok -} - -func (d *differ) IncrementalDiff(existing []*models.RecordConfig) (unchanged, create, toDelete, modify Changeset, err error) { - unchanged = Changeset{} - create = Changeset{} - toDelete = Changeset{} - modify = Changeset{} - desired := d.dc.Records - - //fmt.Printf("********** DEBUG: STARTING IncrementalDiff\n") - - // sort existing and desired by name - - existingByNameAndType := map[models.RecordKey][]*models.RecordConfig{} - desiredByNameAndType := map[models.RecordKey][]*models.RecordConfig{} - - //fmt.Printf("********** DEBUG: existing list %+v\n", existing) - - // Gather the existing records. Skip over any that should be ignored. - for _, e := range existing { - //fmt.Printf("********** DEBUG: existing %v %v %v\n", e.GetLabel(), e.Type, e.GetTargetCombined()) - if d.matchIgnoredName(e.GetLabel(), e.Type) { - //fmt.Printf("Ignoring record %s %s due to IGNORE_NAME\n", e.GetLabel(), e.Type) - printer.Debugf("Ignoring record %s %s due to IGNORE_NAME\n", e.GetLabel(), e.Type) - } else if d.matchIgnoredTarget(e.GetTargetField(), e.Type) { - //fmt.Printf("Ignoring record %s %s due to IGNORE_TARGET\n", e.GetLabel(), e.Type) - printer.Debugf("Ignoring record %s %s due to IGNORE_TARGET\n", e.GetLabel(), e.Type) - } else { - k := e.Key() - existingByNameAndType[k] = append(existingByNameAndType[k], e) - } - } - - // Review the desired records. If we're modifying one that should be ignored, that's an error. - //fmt.Printf("********** DEBUG: desired list %+v\n", desired) - for _, dr := range desired { - //fmt.Printf("********** DEBUG: desired %v %v %v -- %v %v\n", dr.GetLabel(), dr.Type, dr.GetTargetCombined(), apexException(dr), d.matchIgnoredName(dr.GetLabel())) - if d.matchIgnoredName(dr.GetLabel(), dr.Type) { - //if !apexException(dr) || !ignoreNameException(dr) { - if (!ignoreNameException(dr)) && (!apexException(dr)) { - return nil, nil, nil, nil, fmt.Errorf("trying to update/add IGNORE_NAMEd record: %s %s", dr.GetLabel(), dr.Type) - //} else { - // fmt.Printf("********** DEBUG: desired EXCEPTION\n") - } - } else if d.matchIgnoredTarget(dr.GetTargetField(), dr.Type) { - return nil, nil, nil, nil, fmt.Errorf("trying to update/add IGNORE_TARGETd record: %s %s", dr.GetLabel(), dr.Type) - } else { - k := dr.Key() - desiredByNameAndType[k] = append(desiredByNameAndType[k], dr) - } - } - // if NO_PURGE is set, just remove anything that is only in existing. - if d.dc.KeepUnknown { - for k := range existingByNameAndType { - if _, ok := desiredByNameAndType[k]; !ok { - printer.Debugf("Ignoring record set %s %s due to NO_PURGE\n", k.Type, k.NameFQDN) - delete(existingByNameAndType, k) - } - } - } - // Look through existing records. This will give us changes and deletions and some additions. - // Each iteration is only for a single type/name record set - for key, existingRecords := range existingByNameAndType { - desiredRecords := desiredByNameAndType[key] - - // Very first, get rid of any identical records. Easy. - for i := len(existingRecords) - 1; i >= 0; i-- { - ex := existingRecords[i] - for j, de := range desiredRecords { - if d.content(de) != d.content(ex) { - continue - } - unchanged = append(unchanged, Correlation{d, ex, de}) - existingRecords = existingRecords[:i+copy(existingRecords[i:], existingRecords[i+1:])] - desiredRecords = desiredRecords[:j+copy(desiredRecords[j:], desiredRecords[j+1:])] - break - } - } - - // Next, match by target. This will give the most natural modifications. - for i := len(existingRecords) - 1; i >= 0; i-- { - ex := existingRecords[i] - for j, de := range desiredRecords { - if de.GetTargetField() == ex.GetTargetField() { - // two records share a target, but different content (ttl or metadata changes) - modify = append(modify, Correlation{d, ex, de}) - // remove from both slices by index - existingRecords = existingRecords[:i+copy(existingRecords[i:], existingRecords[i+1:])] - desiredRecords = desiredRecords[:j+copy(desiredRecords[j:], desiredRecords[j+1:])] - break - } - } - } - - desiredLookup := map[string]*models.RecordConfig{} - existingLookup := map[string]*models.RecordConfig{} - // build index based on normalized content data - for _, ex := range existingRecords { - normalized := d.content(ex) - //fmt.Printf("DEBUG: normalized: %v\n", normalized) - // NB(tlim): Commenting this out. If the provider is returning - // records that are exact duplicates, that's bad and against the - // RFCs. However, we shouldn't error out. Instead, we should - // continue so that we can delete them. Experience shows one - // record will be deleted per iteration but at least the problem - // will fix itself that way. Erroring out means it will require - // manually fixing (going to the control panel, deleting - // individual records, etc.) - //if existingLookup[normalized] != nil { - // return nil, nil, nil, nil, fmt.Errorf("DUPLICATE E_RECORD FOUND: %s %s", key, normalized) - //} - existingLookup[normalized] = ex - } - for _, de := range desiredRecords { - normalized := d.content(de) - if desiredLookup[normalized] != nil { - return nil, nil, nil, nil, fmt.Errorf("DUPLICATE D_RECORD FOUND: %s %s", key, normalized) - } - desiredLookup[normalized] = de - } - // if a record is in both, it is unchanged - for norm, ex := range existingLookup { - if de, ok := desiredLookup[norm]; ok { - unchanged = append(unchanged, Correlation{d, ex, de}) - delete(existingLookup, norm) - delete(desiredLookup, norm) - } - } - // sort records by normalized text. Keeps behaviour deterministic - existingStrings, desiredStrings := sortedKeys(existingLookup), sortedKeys(desiredLookup) - // Modifications. Take 1 from each side. - for len(desiredStrings) > 0 && len(existingStrings) > 0 { - modify = append(modify, Correlation{d, existingLookup[existingStrings[0]], desiredLookup[desiredStrings[0]]}) - existingStrings = existingStrings[1:] - desiredStrings = desiredStrings[1:] - } - // If desired still has things they are additions - for _, norm := range desiredStrings { - rec := desiredLookup[norm] - create = append(create, Correlation{d, nil, rec}) - } - // if found, but not desired, delete it - for _, norm := range existingStrings { - rec := existingLookup[norm] - toDelete = append(toDelete, Correlation{d, rec, nil}) - } - // remove this set from the desired list to indicate we have processed it. - delete(desiredByNameAndType, key) - } - - // any name/type sets not already processed are pure additions - for name := range existingByNameAndType { - delete(desiredByNameAndType, name) - } - for _, desiredList := range desiredByNameAndType { - for _, rec := range desiredList { - create = append(create, Correlation{d, nil, rec}) - } - } - - // Sort the lists. This is purely cosmetic. - sort.Slice(unchanged, func(i, j int) bool { return ChangesetLess(unchanged, i, j) }) - sort.Slice(create, func(i, j int) bool { return ChangesetLess(create, i, j) }) - sort.Slice(toDelete, func(i, j int) bool { return ChangesetLess(toDelete, i, j) }) - - return -} +// func apexException(rec *models.RecordConfig) bool { +// // Providers often add NS and SOA records at the apex. These +// // should not be included in certain checks. +// return (rec.Type == "NS" || rec.Type == "SOA") && rec.GetLabel() == "@" +// } + +// func ignoreNameException(rec *models.RecordConfig) bool { +// // People wanted it to be possible to disable this safety check. +// // Ok, here it is. You now have two risks: +// // 1. Two owners (DNSControl and some other entity) toggling a record between two settings. +// // 2. The other owner wiping all records at this label, which won't be noticed until the next time dnscontrol is run. +// //fmt.Printf("********** DEBUG IGNORE %v %v %q\n", rec.GetLabel(), rec.Type, rec.Metadata["ignore_name_disable_safety_check"]) +// // See https://github.com/StackExchange/dnscontrol/issues/1106 +// _, ok := rec.Metadata["ignore_name_disable_safety_check"] +// return ok +// } + +// func (d *differ) IncrementalDiff(existing []*models.RecordConfig) (unchanged, create, toDelete, modify Changeset, err error) { +// unchanged = Changeset{} +// create = Changeset{} +// toDelete = Changeset{} +// modify = Changeset{} +// desired := d.dc.Records + +// //fmt.Printf("********** DEBUG: STARTING IncrementalDiff\n") + +// // sort existing and desired by name + +// existingByNameAndType := map[models.RecordKey][]*models.RecordConfig{} +// desiredByNameAndType := map[models.RecordKey][]*models.RecordConfig{} + +// //fmt.Printf("********** DEBUG: existing list %+v\n", existing) + +// // Gather the existing records. Skip over any that should be ignored. +// for _, e := range existing { +// //fmt.Printf("********** DEBUG: existing %v %v %v\n", e.GetLabel(), e.Type, e.GetTargetCombined()) +// if d.matchIgnoredName(e.GetLabel(), e.Type) { +// //fmt.Printf("Ignoring record %s %s due to IGNORE_NAME\n", e.GetLabel(), e.Type) +// printer.Debugf("Ignoring record %s %s due to IGNORE_NAME\n", e.GetLabel(), e.Type) +// } else if d.matchIgnoredTarget(e.GetTargetField(), e.Type) { +// //fmt.Printf("Ignoring record %s %s due to IGNORE_TARGET\n", e.GetLabel(), e.Type) +// printer.Debugf("Ignoring record %s %s due to IGNORE_TARGET\n", e.GetLabel(), e.Type) +// } else { +// k := e.Key() +// existingByNameAndType[k] = append(existingByNameAndType[k], e) +// } +// } + +// // Review the desired records. If we're modifying one that should be ignored, that's an error. +// //fmt.Printf("********** DEBUG: desired list %+v\n", desired) +// for _, dr := range desired { +// //fmt.Printf("********** DEBUG: desired %v %v %v -- %v %v\n", dr.GetLabel(), dr.Type, dr.GetTargetCombined(), apexException(dr), d.matchIgnoredName(dr.GetLabel())) +// if d.matchIgnoredName(dr.GetLabel(), dr.Type) { +// //if !apexException(dr) || !ignoreNameException(dr) { +// if (!ignoreNameException(dr)) && (!apexException(dr)) { +// return nil, nil, nil, nil, fmt.Errorf("trying to update/add IGNORE_NAMEd record: %s %s", dr.GetLabel(), dr.Type) +// //} else { +// // fmt.Printf("********** DEBUG: desired EXCEPTION\n") +// } +// } else if d.matchIgnoredTarget(dr.GetTargetField(), dr.Type) { +// return nil, nil, nil, nil, fmt.Errorf("trying to update/add IGNORE_TARGETd record: %s %s", dr.GetLabel(), dr.Type) +// } else { +// k := dr.Key() +// desiredByNameAndType[k] = append(desiredByNameAndType[k], dr) +// } +// } +// // if NO_PURGE is set, just remove anything that is only in existing. +// if d.dc.KeepUnknown { +// for k := range existingByNameAndType { +// if _, ok := desiredByNameAndType[k]; !ok { +// printer.Debugf("Ignoring record set %s %s due to NO_PURGE\n", k.Type, k.NameFQDN) +// delete(existingByNameAndType, k) +// } +// } +// } +// // Look through existing records. This will give us changes and deletions and some additions. +// // Each iteration is only for a single type/name record set +// for key, existingRecords := range existingByNameAndType { +// desiredRecords := desiredByNameAndType[key] + +// // Very first, get rid of any identical records. Easy. +// for i := len(existingRecords) - 1; i >= 0; i-- { +// ex := existingRecords[i] +// for j, de := range desiredRecords { +// if d.content(de) != d.content(ex) { +// continue +// } +// unchanged = append(unchanged, Correlation{d, ex, de}) +// existingRecords = existingRecords[:i+copy(existingRecords[i:], existingRecords[i+1:])] +// desiredRecords = desiredRecords[:j+copy(desiredRecords[j:], desiredRecords[j+1:])] +// break +// } +// } + +// // Next, match by target. This will give the most natural modifications. +// for i := len(existingRecords) - 1; i >= 0; i-- { +// ex := existingRecords[i] +// for j, de := range desiredRecords { +// if de.GetTargetField() == ex.GetTargetField() { +// // two records share a target, but different content (ttl or metadata changes) +// modify = append(modify, Correlation{d, ex, de}) +// // remove from both slices by index +// existingRecords = existingRecords[:i+copy(existingRecords[i:], existingRecords[i+1:])] +// desiredRecords = desiredRecords[:j+copy(desiredRecords[j:], desiredRecords[j+1:])] +// break +// } +// } +// } + +// desiredLookup := map[string]*models.RecordConfig{} +// existingLookup := map[string]*models.RecordConfig{} +// // build index based on normalized content data +// for _, ex := range existingRecords { +// normalized := d.content(ex) +// //fmt.Printf("DEBUG: normalized: %v\n", normalized) +// // NB(tlim): Commenting this out. If the provider is returning +// // records that are exact duplicates, that's bad and against the +// // RFCs. However, we shouldn't error out. Instead, we should +// // continue so that we can delete them. Experience shows one +// // record will be deleted per iteration but at least the problem +// // will fix itself that way. Erroring out means it will require +// // manually fixing (going to the control panel, deleting +// // individual records, etc.) +// //if existingLookup[normalized] != nil { +// // return nil, nil, nil, nil, fmt.Errorf("DUPLICATE E_RECORD FOUND: %s %s", key, normalized) +// //} +// existingLookup[normalized] = ex +// } +// for _, de := range desiredRecords { +// normalized := d.content(de) +// if desiredLookup[normalized] != nil { +// return nil, nil, nil, nil, fmt.Errorf("DUPLICATE D_RECORD FOUND: %s %s", key, normalized) +// } +// desiredLookup[normalized] = de +// } +// // if a record is in both, it is unchanged +// for norm, ex := range existingLookup { +// if de, ok := desiredLookup[norm]; ok { +// unchanged = append(unchanged, Correlation{d, ex, de}) +// delete(existingLookup, norm) +// delete(desiredLookup, norm) +// } +// } +// // sort records by normalized text. Keeps behaviour deterministic +// existingStrings, desiredStrings := sortedKeys(existingLookup), sortedKeys(desiredLookup) +// // Modifications. Take 1 from each side. +// for len(desiredStrings) > 0 && len(existingStrings) > 0 { +// modify = append(modify, Correlation{d, existingLookup[existingStrings[0]], desiredLookup[desiredStrings[0]]}) +// existingStrings = existingStrings[1:] +// desiredStrings = desiredStrings[1:] +// } +// // If desired still has things they are additions +// for _, norm := range desiredStrings { +// rec := desiredLookup[norm] +// create = append(create, Correlation{d, nil, rec}) +// } +// // if found, but not desired, delete it +// for _, norm := range existingStrings { +// rec := existingLookup[norm] +// toDelete = append(toDelete, Correlation{d, rec, nil}) +// } +// // remove this set from the desired list to indicate we have processed it. +// delete(desiredByNameAndType, key) +// } + +// // any name/type sets not already processed are pure additions +// for name := range existingByNameAndType { +// delete(desiredByNameAndType, name) +// } +// for _, desiredList := range desiredByNameAndType { +// for _, rec := range desiredList { +// create = append(create, Correlation{d, nil, rec}) +// } +// } + +// // Sort the lists. This is purely cosmetic. +// sort.Slice(unchanged, func(i, j int) bool { return ChangesetLess(unchanged, i, j) }) +// sort.Slice(create, func(i, j int) bool { return ChangesetLess(create, i, j) }) +// sort.Slice(toDelete, func(i, j int) bool { return ChangesetLess(toDelete, i, j) }) + +// return +// } // ChangesetLess returns true if c[i] < c[j]. func ChangesetLess(c Changeset, i, j int) bool { @@ -291,49 +285,49 @@ func CorrectionLess(c []*models.Correction, i, j int) bool { return c[i].Msg < c[j].Msg } -func (d *differ) ChangedGroups(existing []*models.RecordConfig) (map[models.RecordKey][]string, error) { - changedKeys := map[models.RecordKey][]string{} - _, create, toDelete, modify, err := d.IncrementalDiff(existing) - if err != nil { - return nil, err - } - for _, c := range create { - changedKeys[c.Desired.Key()] = append(changedKeys[c.Desired.Key()], c.String()) - } - for _, d := range toDelete { - changedKeys[d.Existing.Key()] = append(changedKeys[d.Existing.Key()], d.String()) - } - for _, m := range modify { - changedKeys[m.Desired.Key()] = append(changedKeys[m.Desired.Key()], m.String()) - } - return changedKeys, nil -} - -// DebugKeyMapMap debug prints the results from ChangedGroups. -func DebugKeyMapMap(note string, m map[models.RecordKey][]string) { - // The output isn't pretty but it is useful. - fmt.Println("DEBUG:", note) - - // Extract the keys - var keys []models.RecordKey - for k := range m { - keys = append(keys, k) - } - sort.SliceStable(keys, func(i, j int) bool { - if keys[i].NameFQDN == keys[j].NameFQDN { - return keys[i].Type < keys[j].Type - } - return keys[i].NameFQDN < keys[j].NameFQDN - }) - - // Pretty print the map: - for _, k := range keys { - fmt.Printf(" %v %v:\n", k.Type, k.NameFQDN) - for _, s := range m[k] { - fmt.Printf(" -- %q\n", s) - } - } -} +// func (d *differ) ChangedGroups(existing []*models.RecordConfig) (map[models.RecordKey][]string, error) { +// changedKeys := map[models.RecordKey][]string{} +// _, create, toDelete, modify, err := d.IncrementalDiff(existing) +// if err != nil { +// return nil, err +// } +// for _, c := range create { +// changedKeys[c.Desired.Key()] = append(changedKeys[c.Desired.Key()], c.String()) +// } +// for _, d := range toDelete { +// changedKeys[d.Existing.Key()] = append(changedKeys[d.Existing.Key()], d.String()) +// } +// for _, m := range modify { +// changedKeys[m.Desired.Key()] = append(changedKeys[m.Desired.Key()], m.String()) +// } +// return changedKeys, nil +// } + +// // DebugKeyMapMap debug prints the results from ChangedGroups. +// func DebugKeyMapMap(note string, m map[models.RecordKey][]string) { +// // The output isn't pretty but it is useful. +// fmt.Println("DEBUG:", note) + +// // Extract the keys +// var keys []models.RecordKey +// for k := range m { +// keys = append(keys, k) +// } +// sort.SliceStable(keys, func(i, j int) bool { +// if keys[i].NameFQDN == keys[j].NameFQDN { +// return keys[i].Type < keys[j].Type +// } +// return keys[i].NameFQDN < keys[j].NameFQDN +// }) + +// // Pretty print the map: +// for _, k := range keys { +// fmt.Printf(" %v %v:\n", k.Type, k.NameFQDN) +// for _, s := range m[k] { +// fmt.Printf(" -- %q\n", s) +// } +// } +// } func (c Correlation) String() string { if c.Existing == nil { @@ -345,84 +339,84 @@ func (c Correlation) String() string { return color.YellowString("± MODIFY %s %s: (%s) -> (%s)", c.Existing.Type, c.Existing.GetLabelFQDN(), c.d.content(c.Existing), c.d.content(c.Desired)) } -func sortedKeys(m map[string]*models.RecordConfig) []string { - s := []string{} - for v := range m { - s = append(s, v) - } - sort.Strings(s) - return s -} - -var spaceCommaTokenizerRegexp = regexp.MustCompile(`\s*,\s*`) - -func compileIgnoredNames(ignoredNames []*models.IgnoreName) []ignoredName { - result := make([]ignoredName, 0, len(ignoredNames)) - - for _, tst := range ignoredNames { - g, err := glob.Compile(tst.Pattern, '.') - if err != nil { - panic(fmt.Sprintf("Failed to compile IGNORE_NAME pattern %q: %v", tst.Pattern, err)) - } - - t := []string{} - if tst.Types != "" { - t = spaceCommaTokenizerRegexp.Split(tst.Types, -1) - } - - result = append(result, ignoredName{nameGlob: g, rTypes: t}) - } - - return result -} - -func compileIgnoredTargets(ignoredTargets []*models.IgnoreTarget) []glob.Glob { - result := make([]glob.Glob, 0, len(ignoredTargets)) - - for _, tst := range ignoredTargets { - if tst.Type != "CNAME" { - panic(fmt.Sprintf("Invalid rType for IGNORE_TARGET %v", tst.Type)) - } - - g, err := glob.Compile(tst.Pattern, '.') - if err != nil { - panic(fmt.Sprintf("Failed to compile IGNORE_TARGET pattern %q: %v", tst, err)) - } - - result = append(result, g) - } - - return result -} - -func (d *differ) matchIgnoredName(name string, rType string) bool { - for _, tst := range d.compiledIgnoredNames { - //fmt.Printf("********** DEBUG: matchIgnoredName %q %q %v %v\n", name, rType, tst, tst.nameGlob.Match(name)) - if tst.nameGlob.Match(name) { - if tst.rTypes == nil { - return true - } - - for _, rt := range tst.rTypes { - if rt == "*" || rt == rType { - return true - } - } - } - } - return false -} - -func (d *differ) matchIgnoredTarget(target string, rType string) bool { - if rType != "CNAME" { - return false - } - - for _, tst := range d.compiledIgnoredTargets { - if tst.Match(target) { - return true - } - } - - return false -} +// func sortedKeys(m map[string]*models.RecordConfig) []string { +// s := []string{} +// for v := range m { +// s = append(s, v) +// } +// sort.Strings(s) +// return s +// } + +// var spaceCommaTokenizerRegexp = regexp.MustCompile(`\s*,\s*`) + +// func compileIgnoredNames(ignoredNames []*models.IgnoreName) []ignoredName { +// result := make([]ignoredName, 0, len(ignoredNames)) + +// for _, tst := range ignoredNames { +// g, err := glob.Compile(tst.Pattern, '.') +// if err != nil { +// panic(fmt.Sprintf("Failed to compile IGNORE_NAME pattern %q: %v", tst.Pattern, err)) +// } + +// t := []string{} +// if tst.Types != "" { +// t = spaceCommaTokenizerRegexp.Split(tst.Types, -1) +// } + +// result = append(result, ignoredName{nameGlob: g, rTypes: t}) +// } + +// return result +// } + +// func compileIgnoredTargets(ignoredTargets []*models.IgnoreTarget) []glob.Glob { +// result := make([]glob.Glob, 0, len(ignoredTargets)) + +// for _, tst := range ignoredTargets { +// if tst.Type != "CNAME" { +// panic(fmt.Sprintf("Invalid rType for IGNORE_TARGET %v", tst.Type)) +// } + +// g, err := glob.Compile(tst.Pattern, '.') +// if err != nil { +// panic(fmt.Sprintf("Failed to compile IGNORE_TARGET pattern %q: %v", tst, err)) +// } + +// result = append(result, g) +// } + +// return result +// } + +// func (d *differ) matchIgnoredName(name string, rType string) bool { +// for _, tst := range d.compiledIgnoredNames { +// //fmt.Printf("********** DEBUG: matchIgnoredName %q %q %v %v\n", name, rType, tst, tst.nameGlob.Match(name)) +// if tst.nameGlob.Match(name) { +// if tst.rTypes == nil { +// return true +// } + +// for _, rt := range tst.rTypes { +// if rt == "*" || rt == rType { +// return true +// } +// } +// } +// } +// return false +// } + +// func (d *differ) matchIgnoredTarget(target string, rType string) bool { +// if rType != "CNAME" { +// return false +// } + +// for _, tst := range d.compiledIgnoredTargets { +// if tst.Match(target) { +// return true +// } +// } + +// return false +// } diff --git a/pkg/diff/diff2compat.go b/pkg/diff/diff2compat.go index f848ebf2dd..d747a9b7b4 100644 --- a/pkg/diff/diff2compat.go +++ b/pkg/diff/diff2compat.go @@ -24,9 +24,9 @@ func NewCompat(dc *models.DomainConfig, extraValues ...func(*models.RecordConfig panic("extraValues not supported") } - d := New(dc) + // d := New(dc) return &differCompat{ - OldDiffer: d.(*differ), + //OldDiffer: d.(*differ), dc: dc, } @@ -35,8 +35,6 @@ func NewCompat(dc *models.DomainConfig, extraValues ...func(*models.RecordConfig // differCompat meets the Differ interface but provides its service // using pkg/diff2 instead of pkg/diff. type differCompat struct { - OldDiffer *differ // Store the backwards-compatible "d" for pkg/diff - dc *models.DomainConfig } @@ -57,7 +55,7 @@ func (d *differCompat) IncrementalDiff(existing []*models.RecordConfig) (unchang } for _, inst := range instructions { - cor := Correlation{d: d.OldDiffer} + cor := Correlation{} switch inst.Type { case diff2.REPORT: // Sadly the NewCompat function doesn't have an equivalent. We diff --git a/pkg/diff/diff_test.go b/pkg/diff/diff_test.go index 6ea1832458..76969bcfdb 100644 --- a/pkg/diff/diff_test.go +++ b/pkg/diff/diff_test.go @@ -1,374 +1,365 @@ package diff -import ( - "fmt" - "strconv" - "strings" - "testing" - - "github.com/StackExchange/dnscontrol/v4/models" -) - -func myRecord(s string) *models.RecordConfig { - parts := strings.Split(s, " ") - ttl, _ := strconv.ParseUint(parts[2], 10, 32) - r := &models.RecordConfig{ - Type: parts[1], - TTL: uint32(ttl), - Metadata: map[string]string{}, - } - r.SetLabel(parts[0], "example.com") - r.SetTarget(parts[3]) - return r -} - -func TestAdditionsOnly(t *testing.T) { - desired := []*models.RecordConfig{ - myRecord("@ A 1 1.2.3.4"), - } - existing := []*models.RecordConfig{} - checkLengths(t, existing, desired, 0, 1, 0, 0) -} - -func TestDeletionsOnly(t *testing.T) { - existing := []*models.RecordConfig{ - myRecord("@ A 1 1.2.3.4"), - } - desired := []*models.RecordConfig{} - checkLengths(t, existing, desired, 0, 0, 1, 0) -} - -func TestModification(t *testing.T) { - existing := []*models.RecordConfig{ - myRecord("www A 1 1.1.1.1"), - myRecord("@ A 1 1.2.3.4"), - } - desired := []*models.RecordConfig{ - myRecord("@ A 32 1.2.3.4"), - myRecord("www A 1 1.1.1.1"), - } - un, _, _, mod := checkLengths(t, existing, desired, 1, 0, 0, 1) - if un[0].Desired != desired[1] || un[0].Existing != existing[0] { - t.Error("Expected unchanged records to be correlated") - } - if mod[0].Desired != desired[0] || mod[0].Existing != existing[1] { - t.Errorf("Expected modified records to be correlated") - } -} - -func TestUnchangedWithAddition(t *testing.T) { - existing := []*models.RecordConfig{ - myRecord("www A 1 1.1.1.1"), - } - desired := []*models.RecordConfig{ - myRecord("www A 1 1.2.3.4"), - myRecord("www A 1 1.1.1.1"), - } - un, _, _, _ := checkLengths(t, existing, desired, 1, 1, 0, 0) - if un[0].Desired != desired[1] || un[0].Existing != existing[0] { - t.Errorf("Expected unchanged records to be correlated") - } -} - -// s stringifies a RecordConfig for testing purposes. -func s(rc *models.RecordConfig) string { - return fmt.Sprintf("%s %s %d %s", rc.GetLabel(), rc.Type, rc.TTL, rc.GetTargetCombined()) -} - -func TestOutOfOrderRecords(t *testing.T) { - existing := []*models.RecordConfig{ - myRecord("www A 1 1.1.1.1"), - myRecord("www A 1 2.2.2.2"), - myRecord("www A 1 3.3.3.3"), - } - desired := []*models.RecordConfig{ - myRecord("www A 1 1.1.1.1"), - myRecord("www A 1 2.2.2.2"), - myRecord("www A 1 2.2.2.3"), - myRecord("www A 10 3.3.3.3"), - } - _, _, _, mods := checkLengths(t, existing, desired, 2, 1, 0, 1) - if s(mods[0].Desired) != s(desired[3]) || s(mods[0].Existing) != s(existing[2]) { - t.Fatalf("Expected to match %s and %s, but matched %s and %s", s(existing[2]), s(desired[3]), s(mods[0].Existing), s(mods[0].Desired)) - } -} - -func TestMxPrio(t *testing.T) { - existing := []*models.RecordConfig{ - myRecord("www MX 1 1.1.1.1"), - } - desired := []*models.RecordConfig{ - myRecord("www MX 1 1.1.1.1"), - } - existing[0].MxPreference = 10 - desired[0].MxPreference = 20 - checkLengths(t, existing, desired, 0, 0, 0, 1) -} - -func TestTTLChange(t *testing.T) { - existing := []*models.RecordConfig{ - myRecord("www MX 1 1.1.1.1"), - } - desired := []*models.RecordConfig{ - myRecord("www MX 10 1.1.1.1"), - } - checkLengths(t, existing, desired, 0, 0, 0, 1) -} - -func TestMetaChange(t *testing.T) { - existing := []*models.RecordConfig{ - myRecord("www MX 1 1.1.1.1"), - } - desired := []*models.RecordConfig{ - myRecord("www MX 1 1.1.1.1"), - } - existing[0].Metadata["k"] = "aa" - desired[0].Metadata["k"] = "bb" - checkLengths(t, existing, desired, 1, 0, 0, 0) - getMeta := func(r *models.RecordConfig) map[string]string { - return map[string]string{ - "k": r.Metadata["k"], - } - } - checkLengths(t, existing, desired, 0, 0, 0, 1, getMeta) -} - -func TestMetaOrdering(t *testing.T) { - existing := []*models.RecordConfig{ - myRecord("www MX 1 1.1.1.1"), - } - desired := []*models.RecordConfig{ - myRecord("www MX 1 1.1.1.1"), - } - existing[0].Metadata["k"] = "aa" - existing[0].Metadata["x"] = "cc" - desired[0].Metadata["k"] = "aa" - desired[0].Metadata["x"] = "cc" - checkLengths(t, existing, desired, 1, 0, 0, 0) - getMeta := func(r *models.RecordConfig) map[string]string { - return map[string]string{ - "k": r.Metadata["k"], - } - } - checkLengths(t, existing, desired, 1, 0, 0, 0, getMeta) -} - -func checkLengths(t *testing.T, existing, desired []*models.RecordConfig, unCount, createCount, delCount, modCount int, valFuncs ...func(*models.RecordConfig) map[string]string) (un, cre, del, mod Changeset) { - return checkLengthsWithKeepUnknown(t, existing, desired, unCount, createCount, delCount, modCount, false, valFuncs...) -} - -func checkLengthsWithKeepUnknown(t *testing.T, existing, desired []*models.RecordConfig, unCount, createCount, delCount, modCount int, keepUnknown bool, valFuncs ...func(*models.RecordConfig) map[string]string) (un, cre, del, mod Changeset) { - return checkLengthsFull(t, existing, desired, unCount, createCount, delCount, modCount, keepUnknown, []*models.IgnoreName{}, nil, valFuncs...) -} - -func checkLengthsFull(t *testing.T, existing, desired []*models.RecordConfig, unCount, createCount, delCount, modCount int, keepUnknown bool, ignoredRecords []*models.IgnoreName, ignoredTargets []*models.IgnoreTarget, valFuncs ...func(*models.RecordConfig) map[string]string) (un, cre, del, mod Changeset) { - dc := &models.DomainConfig{ - Name: "example.com", - Records: desired, - KeepUnknown: keepUnknown, - IgnoredNames: ignoredRecords, - IgnoredTargets: ignoredTargets, - } - d := New(dc, valFuncs...) - un, cre, del, mod, err := d.IncrementalDiff(existing) - if err != nil { - panic(err) - } - if len(un) != unCount { - t.Errorf("Got %d unchanged records, but expected %d", len(un), unCount) - } - if len(cre) != createCount { - t.Errorf("Got %d records to create, but expected %d", len(cre), createCount) - } - if len(del) != delCount { - t.Errorf("Got %d records to delete, but expected %d", len(del), delCount) - } - if len(mod) != modCount { - t.Errorf("Got %d records to modify, but expected %d", len(mod), modCount) - } - if t.Failed() { - t.FailNow() - } - return -} - -func TestNoPurge(t *testing.T) { - existing := []*models.RecordConfig{ - myRecord("www MX 1 1.1.1.1"), - myRecord("www MX 1 2.2.2.2"), - myRecord("www2 MX 1 1.1.1.1"), - } - desired := []*models.RecordConfig{ - myRecord("www MX 1 1.1.1.1"), - } - checkLengthsWithKeepUnknown(t, existing, desired, 1, 0, 1, 0, true) -} - -func TestIgnoredRecords(t *testing.T) { - existing := []*models.RecordConfig{ - myRecord("www1 A 1 1.1.1.1"), - myRecord("www1 MX 1 1.1.1.1"), - myRecord("www2 A 1 1.1.1.1"), - myRecord("www2 CNAME 1 www"), - myRecord("www2 MX 1 1.1.1.1"), - myRecord("www3 MX 1 1.1.1.1"), - } - desired := []*models.RecordConfig{ - myRecord("www3 MX 1 2.2.2.2"), - } - checkLengthsFull(t, existing, desired, 0, 0, 0, 1, false, - []*models.IgnoreName{ - {Pattern: "www1", Types: "*"}, - {Pattern: "www2", Types: "A,MX, CNAME"}, - }, - nil, - ) -} - -func TestModifyingIgnoredRecords(t *testing.T) { - existing := []*models.RecordConfig{ - myRecord("www1 MX 1 1.1.1.1"), - myRecord("www2 MX 1 1.1.1.1"), - myRecord("www3 MX 1 1.1.1.1"), - } - desired := []*models.RecordConfig{ - myRecord("www2 MX 1 2.2.2.2"), - } - - defer func() { - if r := recover(); r == nil { - t.Errorf("should panic: modification of IGNOREd record") - } - }() - - checkLengthsFull(t, existing, desired, 0, 0, 0, 1, false, - []*models.IgnoreName{{Pattern: "www1", Types: "MX"}, {Pattern: "www2", Types: "*"}}, - nil, - ) -} - -func TestGlobIgnoredName(t *testing.T) { - existing := []*models.RecordConfig{ - myRecord("www1 MX 1 1.1.1.1"), - myRecord("foo.www2 MX 1 1.1.1.1"), - myRecord("foo.bar.www3 MX 1 1.1.1.1"), - myRecord("www4 MX 1 1.1.1.1"), - } - desired := []*models.RecordConfig{ - myRecord("www4 MX 1 2.2.2.2"), - } - checkLengthsFull(t, existing, desired, 0, 0, 0, 1, false, - []*models.IgnoreName{ - {Pattern: "www1", Types: "*"}, - {Pattern: "*.www2", Types: "*"}, - {Pattern: "**.www3", Types: "*"}, - }, - nil, - ) -} - -func TestInvalidGlobIgnoredName(t *testing.T) { - existing := []*models.RecordConfig{ - myRecord("www1 MX 1 1.1.1.1"), - myRecord("www2 MX 1 1.1.1.1"), - myRecord("www3 MX 1 1.1.1.1"), - } - desired := []*models.RecordConfig{ - myRecord("www4 MX 1 2.2.2.2"), - } - - defer func() { - if r := recover(); r == nil { - t.Errorf("should panic: invalid glob pattern for IGNORE_NAME") - } - }() - - checkLengthsFull(t, existing, desired, 0, 1, 0, 0, false, - []*models.IgnoreName{{Pattern: "www1"}, {Pattern: "*.www2"}, {Pattern: "[.www3"}}, - nil, - ) -} - -func TestGlobIgnoredTarget(t *testing.T) { - existing := []*models.RecordConfig{ - myRecord("www1 CNAME 1 ignoreme.com"), - myRecord("foo.www2 MX 1 1.1.1.2"), - myRecord("foo.bar.www3 MX 1 1.1.1.1"), - myRecord("www4 MX 1 1.1.1.1"), - } - desired := []*models.RecordConfig{ - myRecord("foo.www2 MX 1 1.1.1.2"), - myRecord("foo.bar.www3 MX 1 1.1.1.1"), - myRecord("www4 MX 1 2.2.2.2"), - } - checkLengthsFull(t, existing, desired, 2, 0, 0, 1, false, nil, []*models.IgnoreTarget{{Pattern: "ignoreme.com", Type: "CNAME"}}) -} - -func TestInvalidGlobIgnoredTarget(t *testing.T) { - existing := []*models.RecordConfig{ - myRecord("www1 MX 1 1.1.1.1"), - myRecord("www2 MX 1 1.1.1.1"), - myRecord("www3 MX 1 1.1.1.1"), - } - desired := []*models.RecordConfig{ - myRecord("www4 MX 1 2.2.2.2"), - } - - defer func() { - if r := recover(); r == nil { - t.Errorf("should panic: invalid glob pattern for IGNORE_TARGET") - } - }() - - checkLengthsFull(t, existing, desired, 0, 1, 0, 0, false, nil, []*models.IgnoreTarget{{Pattern: "[.www3", Type: "CNAME"}}) -} - -func TestInvalidTypeIgnoredTarget(t *testing.T) { - existing := []*models.RecordConfig{ - myRecord("www1 MX 1 1.1.1.1"), - myRecord("www2 MX 1 1.1.1.1"), - myRecord("www3 MX 1 1.1.1.1"), - } - desired := []*models.RecordConfig{ - myRecord("www4 MX 1 2.2.2.2"), - } - - defer func() { - if r := recover(); r == nil { - t.Errorf("should panic: Invalid rType for IGNORE_TARGET A") - } - }() - - checkLengthsFull(t, existing, desired, 0, 1, 0, 0, false, nil, []*models.IgnoreTarget{{Pattern: "1.1.1.1", Type: "A"}}) -} - -// from https://github.com/StackExchange/dnscontrol/issues/552 -func TestCaas(t *testing.T) { - existing := []*models.RecordConfig{ - myRecord("test CAA 1 1.1.1.1"), - myRecord("test CAA 1 1.1.1.1"), - myRecord("test CAA 1 1.1.1.1"), - } - desired := []*models.RecordConfig{ - myRecord("test CAA 1 1.1.1.1"), - myRecord("test CAA 1 1.1.1.1"), - myRecord("test CAA 1 1.1.1.1"), - } - existing[0].SetTargetCAA(3, "issue", "letsencrypt.org.") - existing[1].SetTargetCAA(3, "issue", "amazon.com.") - existing[2].SetTargetCAA(3, "issuewild", "letsencrypt.org.") - - // this will pass or fail depending on the ordering. Not ok. - desired[0].SetTargetCAA(3, "issue", "letsencrypt.org.") - desired[1].SetTargetCAA(3, "issue", "amazon.com.") - desired[2].SetTargetCAA(3, "issuewild", "letsencrypt.org.") - - checkLengthsFull(t, existing, desired, 3, 0, 0, 0, false, nil, nil) - - // Make sure it passes with a different ordering. Not ok. - desired[2].SetTargetCAA(3, "issue", "letsencrypt.org.") - desired[1].SetTargetCAA(3, "issue", "amazon.com.") - desired[0].SetTargetCAA(3, "issuewild", "letsencrypt.org.") - - checkLengthsFull(t, existing, desired, 3, 0, 0, 0, false, nil, nil) -} +// func myRecord(s string) *models.RecordConfig { +// parts := strings.Split(s, " ") +// ttl, _ := strconv.ParseUint(parts[2], 10, 32) +// r := &models.RecordConfig{ +// Type: parts[1], +// TTL: uint32(ttl), +// Metadata: map[string]string{}, +// } +// r.SetLabel(parts[0], "example.com") +// r.SetTarget(parts[3]) +// return r +// } + +// func TestAdditionsOnly(t *testing.T) { +// desired := []*models.RecordConfig{ +// myRecord("@ A 1 1.2.3.4"), +// } +// existing := []*models.RecordConfig{} +// checkLengths(t, existing, desired, 0, 1, 0, 0) +// } + +// func TestDeletionsOnly(t *testing.T) { +// existing := []*models.RecordConfig{ +// myRecord("@ A 1 1.2.3.4"), +// } +// desired := []*models.RecordConfig{} +// checkLengths(t, existing, desired, 0, 0, 1, 0) +// } + +// func TestModification(t *testing.T) { +// existing := []*models.RecordConfig{ +// myRecord("www A 1 1.1.1.1"), +// myRecord("@ A 1 1.2.3.4"), +// } +// desired := []*models.RecordConfig{ +// myRecord("@ A 32 1.2.3.4"), +// myRecord("www A 1 1.1.1.1"), +// } +// un, _, _, mod := checkLengths(t, existing, desired, 1, 0, 0, 1) +// if un[0].Desired != desired[1] || un[0].Existing != existing[0] { +// t.Error("Expected unchanged records to be correlated") +// } +// if mod[0].Desired != desired[0] || mod[0].Existing != existing[1] { +// t.Errorf("Expected modified records to be correlated") +// } +// } + +// func TestUnchangedWithAddition(t *testing.T) { +// existing := []*models.RecordConfig{ +// myRecord("www A 1 1.1.1.1"), +// } +// desired := []*models.RecordConfig{ +// myRecord("www A 1 1.2.3.4"), +// myRecord("www A 1 1.1.1.1"), +// } +// un, _, _, _ := checkLengths(t, existing, desired, 1, 1, 0, 0) +// if un[0].Desired != desired[1] || un[0].Existing != existing[0] { +// t.Errorf("Expected unchanged records to be correlated") +// } +// } + +// // s stringifies a RecordConfig for testing purposes. +// func s(rc *models.RecordConfig) string { +// return fmt.Sprintf("%s %s %d %s", rc.GetLabel(), rc.Type, rc.TTL, rc.GetTargetCombined()) +// } + +// func TestOutOfOrderRecords(t *testing.T) { +// existing := []*models.RecordConfig{ +// myRecord("www A 1 1.1.1.1"), +// myRecord("www A 1 2.2.2.2"), +// myRecord("www A 1 3.3.3.3"), +// } +// desired := []*models.RecordConfig{ +// myRecord("www A 1 1.1.1.1"), +// myRecord("www A 1 2.2.2.2"), +// myRecord("www A 1 2.2.2.3"), +// myRecord("www A 10 3.3.3.3"), +// } +// _, _, _, mods := checkLengths(t, existing, desired, 2, 1, 0, 1) +// if s(mods[0].Desired) != s(desired[3]) || s(mods[0].Existing) != s(existing[2]) { +// t.Fatalf("Expected to match %s and %s, but matched %s and %s", s(existing[2]), s(desired[3]), s(mods[0].Existing), s(mods[0].Desired)) +// } +// } + +// func TestMxPrio(t *testing.T) { +// existing := []*models.RecordConfig{ +// myRecord("www MX 1 1.1.1.1"), +// } +// desired := []*models.RecordConfig{ +// myRecord("www MX 1 1.1.1.1"), +// } +// existing[0].MxPreference = 10 +// desired[0].MxPreference = 20 +// checkLengths(t, existing, desired, 0, 0, 0, 1) +// } + +// func TestTTLChange(t *testing.T) { +// existing := []*models.RecordConfig{ +// myRecord("www MX 1 1.1.1.1"), +// } +// desired := []*models.RecordConfig{ +// myRecord("www MX 10 1.1.1.1"), +// } +// checkLengths(t, existing, desired, 0, 0, 0, 1) +// } + +// func TestMetaChange(t *testing.T) { +// existing := []*models.RecordConfig{ +// myRecord("www MX 1 1.1.1.1"), +// } +// desired := []*models.RecordConfig{ +// myRecord("www MX 1 1.1.1.1"), +// } +// existing[0].Metadata["k"] = "aa" +// desired[0].Metadata["k"] = "bb" +// checkLengths(t, existing, desired, 1, 0, 0, 0) +// getMeta := func(r *models.RecordConfig) map[string]string { +// return map[string]string{ +// "k": r.Metadata["k"], +// } +// } +// checkLengths(t, existing, desired, 0, 0, 0, 1, getMeta) +// } +// +// func TestMetaOrdering(t *testing.T) { +// existing := []*models.RecordConfig{ +// myRecord("www MX 1 1.1.1.1"), +// } +// desired := []*models.RecordConfig{ +// myRecord("www MX 1 1.1.1.1"), +// } +// existing[0].Metadata["k"] = "aa" +// existing[0].Metadata["x"] = "cc" +// desired[0].Metadata["k"] = "aa" +// desired[0].Metadata["x"] = "cc" +// checkLengths(t, existing, desired, 1, 0, 0, 0) +// getMeta := func(r *models.RecordConfig) map[string]string { +// return map[string]string{ +// "k": r.Metadata["k"], +// } +// } +// checkLengths(t, existing, desired, 1, 0, 0, 0, getMeta) +// } + +// func checkLengths(t *testing.T, existing, desired []*models.RecordConfig, unCount, createCount, delCount, modCount int, valFuncs ...func(*models.RecordConfig) map[string]string) (un, cre, del, mod Changeset) { +// return checkLengthsWithKeepUnknown(t, existing, desired, unCount, createCount, delCount, modCount, false, valFuncs...) +// } + +// func checkLengthsWithKeepUnknown(t *testing.T, existing, desired []*models.RecordConfig, unCount, createCount, delCount, modCount int, keepUnknown bool, valFuncs ...func(*models.RecordConfig) map[string]string) (un, cre, del, mod Changeset) { +// return checkLengthsFull(t, existing, desired, unCount, createCount, delCount, modCount, keepUnknown, []*models.IgnoreName{}, nil, valFuncs...) +// } + +// func checkLengthsFull(t *testing.T, existing, desired []*models.RecordConfig, unCount, createCount, delCount, modCount int, keepUnknown bool, ignoredRecords []*models.IgnoreName, ignoredTargets []*models.IgnoreTarget, valFuncs ...func(*models.RecordConfig) map[string]string) (un, cre, del, mod Changeset) { +// dc := &models.DomainConfig{ +// Name: "example.com", +// Records: desired, +// KeepUnknown: keepUnknown, +// IgnoredNames: ignoredRecords, +// IgnoredTargets: ignoredTargets, +// } +// d := New(dc, valFuncs...) +// un, cre, del, mod, err := d.IncrementalDiff(existing) +// if err != nil { +// panic(err) +// } +// if len(un) != unCount { +// t.Errorf("Got %d unchanged records, but expected %d", len(un), unCount) +// } +// if len(cre) != createCount { +// t.Errorf("Got %d records to create, but expected %d", len(cre), createCount) +// } +// if len(del) != delCount { +// t.Errorf("Got %d records to delete, but expected %d", len(del), delCount) +// } +// if len(mod) != modCount { +// t.Errorf("Got %d records to modify, but expected %d", len(mod), modCount) +// } +// if t.Failed() { +// t.FailNow() +// } +// return +// } + +// func TestNoPurge(t *testing.T) { +// existing := []*models.RecordConfig{ +// myRecord("www MX 1 1.1.1.1"), +// myRecord("www MX 1 2.2.2.2"), +// myRecord("www2 MX 1 1.1.1.1"), +// } +// desired := []*models.RecordConfig{ +// myRecord("www MX 1 1.1.1.1"), +// } +// checkLengthsWithKeepUnknown(t, existing, desired, 1, 0, 1, 0, true) +// } + +// func TestIgnoredRecords(t *testing.T) { +// existing := []*models.RecordConfig{ +// myRecord("www1 A 1 1.1.1.1"), +// myRecord("www1 MX 1 1.1.1.1"), +// myRecord("www2 A 1 1.1.1.1"), +// myRecord("www2 CNAME 1 www"), +// myRecord("www2 MX 1 1.1.1.1"), +// myRecord("www3 MX 1 1.1.1.1"), +// } +// desired := []*models.RecordConfig{ +// myRecord("www3 MX 1 2.2.2.2"), +// } +// checkLengthsFull(t, existing, desired, 0, 0, 0, 1, false, +// []*models.IgnoreName{ +// {Pattern: "www1", Types: "*"}, +// {Pattern: "www2", Types: "A,MX, CNAME"}, +// }, +// nil, +// ) +// } + +// func TestModifyingIgnoredRecords(t *testing.T) { +// existing := []*models.RecordConfig{ +// myRecord("www1 MX 1 1.1.1.1"), +// myRecord("www2 MX 1 1.1.1.1"), +// myRecord("www3 MX 1 1.1.1.1"), +// } +// desired := []*models.RecordConfig{ +// myRecord("www2 MX 1 2.2.2.2"), +// } + +// defer func() { +// if r := recover(); r == nil { +// t.Errorf("should panic: modification of IGNOREd record") +// } +// }() + +// checkLengthsFull(t, existing, desired, 0, 0, 0, 1, false, +// []*models.IgnoreName{{Pattern: "www1", Types: "MX"}, {Pattern: "www2", Types: "*"}}, +// nil, +// ) +// } + +// func TestGlobIgnoredName(t *testing.T) { +// existing := []*models.RecordConfig{ +// myRecord("www1 MX 1 1.1.1.1"), +// myRecord("foo.www2 MX 1 1.1.1.1"), +// myRecord("foo.bar.www3 MX 1 1.1.1.1"), +// myRecord("www4 MX 1 1.1.1.1"), +// } +// desired := []*models.RecordConfig{ +// myRecord("www4 MX 1 2.2.2.2"), +// } +// checkLengthsFull(t, existing, desired, 0, 0, 0, 1, false, +// []*models.IgnoreName{ +// {Pattern: "www1", Types: "*"}, +// {Pattern: "*.www2", Types: "*"}, +// {Pattern: "**.www3", Types: "*"}, +// }, +// nil, +// ) +// } + +// func TestInvalidGlobIgnoredName(t *testing.T) { +// existing := []*models.RecordConfig{ +// myRecord("www1 MX 1 1.1.1.1"), +// myRecord("www2 MX 1 1.1.1.1"), +// myRecord("www3 MX 1 1.1.1.1"), +// } +// desired := []*models.RecordConfig{ +// myRecord("www4 MX 1 2.2.2.2"), +// } + +// defer func() { +// if r := recover(); r == nil { +// t.Errorf("should panic: invalid glob pattern for IGNORE_NAME") +// } +// }() + +// checkLengthsFull(t, existing, desired, 0, 1, 0, 0, false, +// []*models.IgnoreName{{Pattern: "www1"}, {Pattern: "*.www2"}, {Pattern: "[.www3"}}, +// nil, +// ) +// } + +// func TestGlobIgnoredTarget(t *testing.T) { +// existing := []*models.RecordConfig{ +// myRecord("www1 CNAME 1 ignoreme.com"), +// myRecord("foo.www2 MX 1 1.1.1.2"), +// myRecord("foo.bar.www3 MX 1 1.1.1.1"), +// myRecord("www4 MX 1 1.1.1.1"), +// } +// desired := []*models.RecordConfig{ +// myRecord("foo.www2 MX 1 1.1.1.2"), +// myRecord("foo.bar.www3 MX 1 1.1.1.1"), +// myRecord("www4 MX 1 2.2.2.2"), +// } +// checkLengthsFull(t, existing, desired, 2, 0, 0, 1, false, nil, []*models.IgnoreTarget{{Pattern: "ignoreme.com", Type: "CNAME"}}) +// } + +// func TestInvalidGlobIgnoredTarget(t *testing.T) { +// existing := []*models.RecordConfig{ +// myRecord("www1 MX 1 1.1.1.1"), +// myRecord("www2 MX 1 1.1.1.1"), +// myRecord("www3 MX 1 1.1.1.1"), +// } +// desired := []*models.RecordConfig{ +// myRecord("www4 MX 1 2.2.2.2"), +// } + +// defer func() { +// if r := recover(); r == nil { +// t.Errorf("should panic: invalid glob pattern for IGNORE_TARGET") +// } +// }() + +// checkLengthsFull(t, existing, desired, 0, 1, 0, 0, false, nil, []*models.IgnoreTarget{{Pattern: "[.www3", Type: "CNAME"}}) +// } + +// func TestInvalidTypeIgnoredTarget(t *testing.T) { +// existing := []*models.RecordConfig{ +// myRecord("www1 MX 1 1.1.1.1"), +// myRecord("www2 MX 1 1.1.1.1"), +// myRecord("www3 MX 1 1.1.1.1"), +// } +// desired := []*models.RecordConfig{ +// myRecord("www4 MX 1 2.2.2.2"), +// } + +// defer func() { +// if r := recover(); r == nil { +// t.Errorf("should panic: Invalid rType for IGNORE_TARGET A") +// } +// }() + +// checkLengthsFull(t, existing, desired, 0, 1, 0, 0, false, nil, []*models.IgnoreTarget{{Pattern: "1.1.1.1", Type: "A"}}) +// } +// +// // from https://github.com/StackExchange/dnscontrol/issues/552 +// func TestCaas(t *testing.T) { +// existing := []*models.RecordConfig{ +// myRecord("test CAA 1 1.1.1.1"), +// myRecord("test CAA 1 1.1.1.1"), +// myRecord("test CAA 1 1.1.1.1"), +// } +// desired := []*models.RecordConfig{ +// myRecord("test CAA 1 1.1.1.1"), +// myRecord("test CAA 1 1.1.1.1"), +// myRecord("test CAA 1 1.1.1.1"), +// } +// existing[0].SetTargetCAA(3, "issue", "letsencrypt.org.") +// existing[1].SetTargetCAA(3, "issue", "amazon.com.") +// existing[2].SetTargetCAA(3, "issuewild", "letsencrypt.org.") + +// // this will pass or fail depending on the ordering. Not ok. +// desired[0].SetTargetCAA(3, "issue", "letsencrypt.org.") +// desired[1].SetTargetCAA(3, "issue", "amazon.com.") +// desired[2].SetTargetCAA(3, "issuewild", "letsencrypt.org.") + +// checkLengthsFull(t, existing, desired, 3, 0, 0, 0, false, nil, nil) + +// // Make sure it passes with a different ordering. Not ok. +// desired[2].SetTargetCAA(3, "issue", "letsencrypt.org.") +// desired[1].SetTargetCAA(3, "issue", "amazon.com.") +// desired[0].SetTargetCAA(3, "issuewild", "letsencrypt.org.") + +// checkLengthsFull(t, existing, desired, 3, 0, 0, 0, false, nil, nil) +// } diff --git a/pkg/diff2/flag.go b/pkg/diff2/flag.go index f46644e757..3348a79928 100644 --- a/pkg/diff2/flag.go +++ b/pkg/diff2/flag.go @@ -1,7 +1,4 @@ package diff2 -// EnableDiff2 is true to activate the experimental diff2 algorithm. -var EnableDiff2 bool - // DisableOrdering can be set to true to disable the reordering of the changes var DisableOrdering bool diff --git a/pkg/js/helpers.js b/pkg/js/helpers.js index 10dfa1f694..68bb342f34 100644 --- a/pkg/js/helpers.js +++ b/pkg/js/helpers.js @@ -831,16 +831,6 @@ function DISABLE_IGNORE_SAFETY_CHECK(d) { d.unmanaged_disable_safety_check = true; } -var IGNORE_NAME_DISABLE_SAFETY_CHECK = { - ignore_name_disable_safety_check: 'true', - // (NOTE: diff1 only.) - // This disables a safety check intended to prevent: - // 1. Two owners toggling a record between two settings. - // 2. The other owner wiping all records at this label, which won't - // be noticed until the next time dnscontrol is run. - // See https://github.com/StackExchange/dnscontrol/issues/1106 -}; - // IGNORE(labelPattern, rtypePattern, targetPattern) function IGNORE(labelPattern, rtypePattern, targetPattern) { if (labelPattern === undefined) { @@ -853,9 +843,6 @@ function IGNORE(labelPattern, rtypePattern, targetPattern) { targetPattern = '*'; } return function (d) { - // diff1 - d.ignored_names.push({ pattern: labelPattern, types: rtypePattern }); - // diff2 d.unmanaged.push({ label_pattern: labelPattern, rType_pattern: rtypePattern, @@ -870,9 +857,6 @@ function IGNORE_NAME(name, rTypes) { rTypes = '*'; } return function (d) { - // diff1 - d.ignored_names.push({ pattern: name, types: rTypes }); - // diff2 d.unmanaged.push({ label_pattern: name, rType_pattern: rTypes, @@ -882,9 +866,6 @@ function IGNORE_NAME(name, rTypes) { function IGNORE_TARGET(target, rType) { return function (d) { - // diff1 - d.ignored_targets.push({ pattern: target, type: rType }); - // diff2 d.unmanaged.push({ rType_pattern: rType, target_pattern: target, diff --git a/pkg/js/parse_tests/005-ignored-records.json b/pkg/js/parse_tests/005-ignored-records.json index 0a9f748387..2ea0afa028 100644 --- a/pkg/js/parse_tests/005-ignored-records.json +++ b/pkg/js/parse_tests/005-ignored-records.json @@ -7,42 +7,6 @@ "registrar": "none", "dnsProviders": {}, "records": [], - "ignored_names": [ - { - "pattern": "testignore", - "types": "*" - }, - { - "pattern": "testignore2", - "types": "A" - }, - { - "pattern": "testignore3", - "types": "A, CNAME, TXT" - }, - { - "pattern": "testignore4", - "types": "*" - }, - { - "pattern": "legacyignore", - "types": "*" - }, - { - "pattern": "@", - "types": "*" - } - ], - "ignored_targets": [ - { - "pattern": "testtarget", - "type": "CNAME" - }, - { - "pattern": "@", - "type": "CNAME" - } - ], "unmanaged": [ { "label_pattern": "testignore", @@ -84,50 +48,6 @@ "registrar": "none", "dnsProviders": {}, "records": [], - "ignored_names": [ - { - "pattern": "mylabel", - "types": "*" - }, - { - "pattern": "mylabel2", - "types": "" - }, - { - "pattern": "mylabel3", - "types": "" - }, - { - "pattern": "", - "types": "A" - }, - { - "pattern": "", - "types": "A,AAAA" - }, - { - "pattern": "", - "types": "" - }, - { - "pattern": "labelc", - "types": "CNAME" - }, - { - "pattern": "nametest", - "types": "*" - } - ], - "ignored_targets": [ - { - "pattern": "targettest1", - "type": "" - }, - { - "pattern": "targettest2", - "type": "A" - } - ], "unmanaged": [ { "label_pattern": "mylabel", @@ -171,4 +91,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/pkg/js/parse_tests/023-ignored-glob-records.json b/pkg/js/parse_tests/023-ignored-glob-records.json index 8ea43afddb..08ebbce1b7 100644 --- a/pkg/js/parse_tests/023-ignored-glob-records.json +++ b/pkg/js/parse_tests/023-ignored-glob-records.json @@ -7,12 +7,6 @@ "registrar": "none", "dnsProviders": {}, "records": [], - "ignored_names": [ - { - "pattern": "\\*.testignore", - "types": "*" - } - ], "unmanaged": [ { "label_pattern": "\\*.testignore", @@ -22,4 +16,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/pkg/normalize/validate.go b/pkg/normalize/validate.go index 88c9b85788..c04be3e818 100644 --- a/pkg/normalize/validate.go +++ b/pkg/normalize/validate.go @@ -7,7 +7,6 @@ import ( "strings" "github.com/StackExchange/dnscontrol/v4/models" - "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/pkg/transform" "github.com/StackExchange/dnscontrol/v4/providers" "github.com/miekg/dns" @@ -429,11 +428,8 @@ func ValidateAndNormalizeConfig(config *models.DNSConfig) (errs []error) { // Populate FQDN: rec.SetLabel(rec.GetLabel(), domain.Name) - // Warn if a diff1-only feature is used in diff2 mode. - if diff2.EnableDiff2 { - if _, ok := rec.Metadata["ignore_name_disable_safety_check"]; ok { - errs = append(errs, fmt.Errorf("IGNORE_NAME_DISABLE_SAFETY_CHECK no longer supported. Please use DISABLE_IGNORE_SAFETY_CHECK for the entire domain")) - } + if _, ok := rec.Metadata["ignore_name_disable_safety_check"]; ok { + errs = append(errs, fmt.Errorf("IGNORE_NAME_DISABLE_SAFETY_CHECK no longer supported. Please use DISABLE_IGNORE_SAFETY_CHECK for the entire domain")) } } diff --git a/providers/akamaiedgedns/akamaiEdgeDnsProvider.go b/providers/akamaiedgedns/akamaiEdgeDnsProvider.go index fc22577517..82d05430f5 100644 --- a/providers/akamaiedgedns/akamaiEdgeDnsProvider.go +++ b/providers/akamaiedgedns/akamaiEdgeDnsProvider.go @@ -16,7 +16,6 @@ import ( "github.com/StackExchange/dnscontrol/v4/models" "github.com/StackExchange/dnscontrol/v4/pkg/diff" - "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/pkg/printer" "github.com/StackExchange/dnscontrol/v4/pkg/txtutil" "github.com/StackExchange/dnscontrol/v4/providers" @@ -110,13 +109,7 @@ func (a *edgeDNSProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, exi txtutil.SplitSingleLongTxt(existingRecords) var corrections []*models.Correction - var keysToUpdate map[models.RecordKey][]string - var err error - if !diff2.EnableDiff2 { - keysToUpdate, err = (diff.New(dc)).ChangedGroups(existingRecords) - } else { - keysToUpdate, err = (diff.NewCompat(dc)).ChangedGroups(existingRecords) - } + keysToUpdate, err := diff.NewCompat(dc).ChangedGroups(existingRecords) if err != nil { return nil, err } diff --git a/providers/autodns/autoDnsProvider.go b/providers/autodns/autoDnsProvider.go index cd01849667..6da132afe9 100644 --- a/providers/autodns/autoDnsProvider.go +++ b/providers/autodns/autoDnsProvider.go @@ -10,9 +10,7 @@ import ( "strings" "github.com/StackExchange/dnscontrol/v4/models" - "github.com/StackExchange/dnscontrol/v4/pkg/diff" "github.com/StackExchange/dnscontrol/v4/pkg/diff2" - "github.com/StackExchange/dnscontrol/v4/pkg/printer" "github.com/StackExchange/dnscontrol/v4/pkg/txtutil" "github.com/StackExchange/dnscontrol/v4/providers" ) @@ -72,103 +70,7 @@ func (api *autoDNSProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, e domain := dc.Name txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records - var changes []*models.RecordConfig var corrections []*models.Correction - if !diff2.EnableDiff2 { - - differ := diff.New(dc) - unchanged, create, del, modify, err := differ.IncrementalDiff(existingRecords) - if err != nil { - return nil, err - } - - for _, m := range unchanged { - changes = append(changes, m.Desired) - } - - for _, m := range del { - // Just notify, these records don't have to be deleted explicitly - printer.Debugf(m.String()) - } - - for _, m := range create { - printer.Debugf(m.String()) - changes = append(changes, m.Desired) - } - - for _, m := range modify { - printer.Debugf("mod") - printer.Debugf(m.String()) - changes = append(changes, m.Desired) - } - - if len(create) > 0 || len(del) > 0 || len(modify) > 0 { - corrections = append(corrections, - &models.Correction{ - Msg: "Zone update for " + domain, - F: func() error { - zoneTTL := uint32(0) - nameServers := []*models.Nameserver{} - resourceRecords := []*ResourceRecord{} - - for _, record := range changes { - // NS records for the APEX should be handled differently - if record.Type == "NS" && record.Name == "@" { - nameServers = append(nameServers, &models.Nameserver{ - Name: strings.TrimSuffix(record.GetTargetField(), "."), - }) - - zoneTTL = record.TTL - } else { - resourceRecord := &ResourceRecord{ - Name: record.Name, - TTL: int64(record.TTL), - Type: record.Type, - Value: record.GetTargetField(), - } - - if resourceRecord.Name == "@" { - resourceRecord.Name = "" - } - - if record.Type == "MX" { - resourceRecord.Pref = int32(record.MxPreference) - } - - if record.Type == "SRV" { - resourceRecord.Value = fmt.Sprintf( - "%d %d %d %s", - record.SrvPriority, - record.SrvWeight, - record.SrvPort, - record.GetTargetField(), - ) - } - if record.Type == "CAA" { - resourceRecord.Value = fmt.Sprintf("%d %s \"%s\"", - record.CaaFlag, - record.CaaTag, - record.GetTargetField(), - ) - } - - resourceRecords = append(resourceRecords, resourceRecord) - } - } - - err := api.updateZone(domain, resourceRecords, nameServers, zoneTTL) - - if err != nil { - return fmt.Errorf(err.Error()) - } - - return nil - }, - }) - } - - return corrections, nil - } msgs, changed, err := diff2.ByZone(existingRecords, dc, nil) if err != nil { diff --git a/providers/axfrddns/axfrddnsProvider.go b/providers/axfrddns/axfrddnsProvider.go index d670b775ba..b7a1f17cef 100644 --- a/providers/axfrddns/axfrddnsProvider.go +++ b/providers/axfrddns/axfrddnsProvider.go @@ -12,7 +12,6 @@ axfrddns - */ import ( - "bytes" "crypto/tls" "encoding/base64" "encoding/json" @@ -24,7 +23,6 @@ import ( "time" "github.com/StackExchange/dnscontrol/v4/models" - "github.com/StackExchange/dnscontrol/v4/pkg/diff" "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/pkg/printer" "github.com/StackExchange/dnscontrol/v4/pkg/txtutil" @@ -460,108 +458,6 @@ func (c *axfrddnsProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, fo return nil, err } - if !diff2.EnableDiff2 { - - // Legacy code with the old `diff` - - differ := diff.New(dc) - _, create, del, mod, err := differ.IncrementalDiff(foundRecords) - if err != nil { - return nil, err - } - - changes := false - buf := &bytes.Buffer{} - buf2 := &bytes.Buffer{} - - // See comment below about hasNSDeletion. - hasNSDeletion := false - for _, c := range create { - if c.Desired.Type == "NS" && c.Desired.Name == "@" { - hasNSDeletion = true - continue - } - } - for _, c := range del { - if c.Existing.Type == "NS" && c.Existing.Name == "@" { - hasNSDeletion = true - continue - } - } - for _, c := range mod { - if c.Existing.Type == "NS" && c.Existing.Name == "@" { - hasNSDeletion = true - continue - } - } - - if hasNSDeletion { - update.Insert([]dns.RR{dummyNs1}) - } - - for _, change := range del { - changes = true - fmt.Fprintln(buf, change) - update.Remove([]dns.RR{change.Existing.ToRR()}) - } - for _, change := range mod { - changes = true - if c.serverHasBuggyCNAME && change.Desired.Type == "CNAME" { - fmt.Fprintln(buf, change.String()+color.RedString(" (delete)")) - update.Remove([]dns.RR{change.Existing.ToRR()}) - hasTwoCorrections = true - fmt.Fprintln(buf2, change.String()+color.GreenString(" (create)")) - update2.Insert([]dns.RR{change.Desired.ToRR()}) - } else { - fmt.Fprintln(buf, change) - update.Remove([]dns.RR{change.Existing.ToRR()}) - update.Insert([]dns.RR{change.Desired.ToRR()}) - } - } - for _, change := range create { - changes = true - splitted := false - if c.serverHasBuggyCNAME && change.Desired.Type == "CNAME" { - for _, change2 := range del { - if change2.Existing.Name == change.Desired.Name { - splitted = true - break - } - } - } - if splitted { - hasTwoCorrections = true - fmt.Fprintln(buf2, change) - update2.Insert([]dns.RR{change.Desired.ToRR()}) - } else { - fmt.Fprintln(buf, change) - update.Insert([]dns.RR{change.Desired.ToRR()}) - } - } - - if hasNSDeletion { - update.Remove([]dns.RR{dummyNs2}) - } - - if !changes { - return nil, nil - } - - if hasTwoCorrections { - - return []*models.Correction{ - c.BuildCorrection(dc, []string{buf.String()}, update), - c.BuildCorrection(dc, []string{buf2.String()}, update2), - }, nil - - } - - return []*models.Correction{ - c.BuildCorrection(dc, []string{buf.String()}, update), - }, nil - - } - changes, err := diff2.ByRecord(foundRecords, dc, nil) if err != nil { return nil, err diff --git a/providers/azuredns/azureDnsProvider.go b/providers/azuredns/azureDnsProvider.go index 484f1c5724..dd6c3f498a 100644 --- a/providers/azuredns/azureDnsProvider.go +++ b/providers/azuredns/azureDnsProvider.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "fmt" - "sort" "strings" "time" @@ -12,7 +11,6 @@ import ( adns "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns" "github.com/Azure/go-autorest/autorest/to" "github.com/StackExchange/dnscontrol/v4/models" - "github.com/StackExchange/dnscontrol/v4/pkg/diff" "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/pkg/printer" "github.com/StackExchange/dnscontrol/v4/pkg/txtutil" @@ -197,145 +195,8 @@ func (a *azurednsProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, ex txtutil.SplitSingleLongTxt(existingRecords) // Autosplit long TXT records var corrections []*models.Correction - if !diff2.EnableDiff2 { - records := a.rawRecords[dc.Name] - zoneName := a.zoneName[dc.Name] - - differ := diff.New(dc) - namesToUpdate, err := differ.ChangedGroups(existingRecords) - if err != nil { - return nil, err - } - - if len(namesToUpdate) == 0 { - return nil, nil - } - - updates := map[models.RecordKey][]*models.RecordConfig{} - - for k := range namesToUpdate { - updates[k] = nil - for _, rc := range dc.Records { - if rc.Key() == k { - updates[k] = append(updates[k], rc) - } - } - } - - for k, recs := range updates { - if len(recs) == 0 { - var rrset *adns.RecordSet - for _, r := range records { - if strings.TrimSuffix(*r.Properties.Fqdn, ".") == k.NameFQDN { - n1, err := nativeToRecordType(r.Type) - if err != nil { - return nil, err - } - n2, err := nativeToRecordType(to.StringPtr(k.Type)) - if err != nil { - return nil, err - } - if n1 == n2 { - rrset = r - break - } - } - } - if rrset != nil { - corrections = append(corrections, - &models.Correction{ - Msg: strings.Join(namesToUpdate[k], "\n"), - F: func() error { - ctx, cancel := context.WithTimeout(context.Background(), 6000*time.Second) - defer cancel() - rt, err := nativeToRecordType(rrset.Type) - if err != nil { - return err - } - //fmt.Fprintf(os.Stderr, "DEBUG: 1 a.recordsClient.Delete(ctx, %v, %v, %v, %v)\n", *a.resourceGroup, zoneName, *rrset.Name, rt) - _, err = a.recordsClient.Delete(ctx, *a.resourceGroup, zoneName, *rrset.Name, rt, nil) - if err != nil { - return err - } - return nil - }, - }) - } else { - return nil, fmt.Errorf("no record set found to delete. Name: '%s'. Type: '%s'", k.NameFQDN, k.Type) - } - } else { - rrset, recordType, err := a.recordToNative(k, recs) - if err != nil { - return nil, err - } - var recordName string - for _, r := range recs { - i := int64(r.TTL) - rrset.Properties.TTL = &i // TODO: make sure that ttls are consistent within a set - recordName = r.Name - } - - for _, r := range records { - existingRecordType, err := nativeToRecordType(r.Type) - if err != nil { - return nil, err - } - changedRecordType, err := nativeToRecordType(to.StringPtr(k.Type)) - if err != nil { - return nil, err - } - if strings.TrimSuffix(*r.Properties.Fqdn, ".") == k.NameFQDN && (changedRecordType == adns.RecordTypeCNAME || existingRecordType == adns.RecordTypeCNAME) { - if existingRecordType == adns.RecordTypeA || existingRecordType == adns.RecordTypeAAAA || changedRecordType == adns.RecordTypeA || changedRecordType == adns.RecordTypeAAAA { //CNAME cannot coexist with an A or AA - corrections = append(corrections, - &models.Correction{ - Msg: strings.Join(namesToUpdate[k], "\n"), - F: func() error { - ctx, cancel := context.WithTimeout(context.Background(), 6000*time.Second) - defer cancel() - //fmt.Fprintf(os.Stderr, "DEBUG: 2 a.recordsClient.Delete(ctx, %v, %v, %v, %v, nil)\n", *a.resourceGroup, zoneName, recordName, existingRecordType) - _, err := a.recordsClient.Delete(ctx, *a.resourceGroup, zoneName, recordName, existingRecordType, nil) - if err != nil { - return err - } - return nil - }, - }) - } - } - } - - corrections = append(corrections, - &models.Correction{ - Msg: strings.Join(namesToUpdate[k], "\n"), - F: func() error { - ctx, cancel := context.WithTimeout(context.Background(), 6000*time.Second) - defer cancel() - _, err := a.recordsClient.CreateOrUpdate(ctx, *a.resourceGroup, zoneName, recordName, recordType, *rrset, nil) - if err != nil { - return err - } - return nil - }, - }) - } - } - - // Sort the records for cosmetic reasons: It just makes a long list - // of deletes or adds easier to read if they are in sorted order. - // That said, it may be risky to sort them (sort key is the text - // message "Msg") if there are deletes that must happen before adds. - // Reading the above code it isn't clear that any of the updates are - // order-dependent. That said, all the tests pass. - // If in the future this causes a bug, we can either just remove - // this next line, or (even better) put any order-dependent - // operations in a single models.Correction{}. - sort.Slice(corrections, func(i, j int) bool { return diff.CorrectionLess(corrections, i, j) }) - - return corrections, nil - } - - // Azure is a "ByRSet" API. + // Azure is a "ByRecordSet" API. changes, err := diff2.ByRecordSet(existingRecords, dc, nil) if err != nil { diff --git a/providers/bind/bindProvider.go b/providers/bind/bindProvider.go index 444ce6d724..91fbff0629 100644 --- a/providers/bind/bindProvider.go +++ b/providers/bind/bindProvider.go @@ -14,7 +14,6 @@ bind - */ import ( - "bytes" "encoding/json" "fmt" "os" @@ -23,7 +22,6 @@ import ( "time" "github.com/StackExchange/dnscontrol/v4/models" - "github.com/StackExchange/dnscontrol/v4/pkg/diff" "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/pkg/prettyzone" "github.com/StackExchange/dnscontrol/v4/pkg/printer" @@ -213,6 +211,7 @@ func ParseZoneContents(content string, zoneName string, zonefileName string) (mo // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. func (c *bindProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, foundRecords models.Records) ([]*models.Correction, error) { txtutil.SplitSingleLongTxt(dc.Records) + var corrections []*models.Correction changes := false var msg string @@ -241,111 +240,68 @@ func (c *bindProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, foundR c.skipNextSoaIncrease = true } - if !diff2.EnableDiff2 { - - differ := diff.New(dc) - _, create, del, mod, err := differ.IncrementalDiff(foundRecords) - if err != nil { - return nil, err - } - - buf := &bytes.Buffer{} - // Print a list of changes. Generate an actual change that is the zone - - for _, i := range create { - changes = true - if c.zoneFileFound { - fmt.Fprintln(buf, i) - } - } - for _, i := range del { - changes = true - if c.zoneFileFound { - fmt.Fprintln(buf, i) - } - } - for _, i := range mod { - changes = true - if c.zoneFileFound { - fmt.Fprintln(buf, i) - } - } - - if c.zoneFileFound { - msg = fmt.Sprintf("GENERATE_ZONEFILE: '%s'. Changes:\n%s", dc.Name, buf) - } else { - msg = fmt.Sprintf("GENERATE_ZONEFILE: '%s' (new file with %d records)\n", dc.Name, len(create)) - } - - } else { - - var msgs []string - var err error - msgs, changes, err = diff2.ByZone(foundRecords, dc, nil) - if err != nil { - return nil, err - } - //fmt.Printf("DEBUG: BIND changes=%v\n", changes) - msg = strings.Join(msgs, "\n") - + var msgs []string + var err error + msgs, changes, err = diff2.ByZone(foundRecords, dc, nil) + if err != nil { + return nil, err + } + if !changes { + return nil, nil + } + msg = strings.Join(msgs, "\n") + + comments := make([]string, 0, 5) + comments = append(comments, + fmt.Sprintf("generated with dnscontrol %s", time.Now().Format(time.RFC3339)), + ) + if dc.AutoDNSSEC == "on" { + // This does nothing but reminds the user to add the correct + // auto-dnssecc zone statement to named.conf. + // While it is a no-op, it is useful for situations where a zone + // has multiple providers. + comments = append(comments, "Automatic DNSSEC signing requested") } - var corrections []*models.Correction - //fmt.Printf("DEBUG: BIND changes=%v\n", changes) - if changes { - - comments := make([]string, 0, 5) - comments = append(comments, - fmt.Sprintf("generated with dnscontrol %s", time.Now().Format(time.RFC3339)), - ) - if dc.AutoDNSSEC == "on" { - // This does nothing but reminds the user to add the correct - // auto-dnssecc zone statement to named.conf. - // While it is a no-op, it is useful for situations where a zone - // has multiple providers. - comments = append(comments, "Automatic DNSSEC signing requested") - } - - c.zonefile = filepath.Join(c.directory, - makeFileName(c.filenameformat, - dc.Metadata[models.DomainUniqueName], dc.Name, dc.Metadata[models.DomainTag]), - ) - - // We only change the serial number if there is a change. - if !c.skipNextSoaIncrease { - desiredSoa.SoaSerial = nextSerial - } + c.zonefile = filepath.Join(c.directory, + makeFileName(c.filenameformat, + dc.Metadata[models.DomainUniqueName], dc.Name, dc.Metadata[models.DomainTag]), + ) - corrections = append(corrections, - &models.Correction{ - Msg: msg, - F: func() error { - printer.Printf("WRITING ZONEFILE: %v\n", c.zonefile) - fname, err := preprocessFilename(c.zonefile) - if err != nil { - return fmt.Errorf("could not create zonefile: %w", err) - } - zf, err := os.Create(fname) - if err != nil { - return fmt.Errorf("could not create zonefile: %w", err) - } - // Beware that if there are any fake types, then they will - // be commented out on write, but we don't reverse that when - // reading, so there will be a diff on every invocation. - err = prettyzone.WriteZoneFileRC(zf, dc.Records, dc.Name, 0, comments) - - if err != nil { - return fmt.Errorf("failed WriteZoneFile: %w", err) - } - err = zf.Close() - if err != nil { - return fmt.Errorf("closing: %w", err) - } - return nil - }, - }) + // We only change the serial number if there is a change. + if !c.skipNextSoaIncrease { + desiredSoa.SoaSerial = nextSerial } + corrections = append(corrections, + &models.Correction{ + Msg: msg, + F: func() error { + printer.Printf("WRITING ZONEFILE: %v\n", c.zonefile) + fname, err := preprocessFilename(c.zonefile) + if err != nil { + return fmt.Errorf("could not create zonefile: %w", err) + } + zf, err := os.Create(fname) + if err != nil { + return fmt.Errorf("could not create zonefile: %w", err) + } + // Beware that if there are any fake types, then they will + // be commented out on write, but we don't reverse that when + // reading, so there will be a diff on every invocation. + err = prettyzone.WriteZoneFileRC(zf, dc.Records, dc.Name, 0, comments) + + if err != nil { + return fmt.Errorf("failed WriteZoneFile: %w", err) + } + err = zf.Close() + if err != nil { + return fmt.Errorf("closing: %w", err) + } + return nil + }, + }) + return corrections, nil } diff --git a/providers/cloudflare/cloudflareProvider.go b/providers/cloudflare/cloudflareProvider.go index 8837258515..432de0cf97 100644 --- a/providers/cloudflare/cloudflareProvider.go +++ b/providers/cloudflare/cloudflareProvider.go @@ -11,7 +11,6 @@ import ( "golang.org/x/net/idna" "github.com/StackExchange/dnscontrol/v4/models" - "github.com/StackExchange/dnscontrol/v4/pkg/diff" "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/pkg/printer" "github.com/StackExchange/dnscontrol/v4/pkg/transform" @@ -227,106 +226,6 @@ func (c *cloudflareProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, checkNSModifications(dc) var corrections []*models.Correction - if !diff2.EnableDiff2 { - - differ := diff.New(dc, getProxyMetadata) - _, create, del, mod, err := differ.IncrementalDiff(records) - if err != nil { - return nil, err - } - - corrections := []*models.Correction{} - - for _, d := range del { - ex := d.Existing - if ex.Type == "PAGE_RULE" { - corrections = append(corrections, &models.Correction{ - Msg: d.String(), - F: func() error { return c.deletePageRule(ex.Original.(cloudflare.PageRule).ID, domainID) }, - }) - } else if ex.Type == "WORKER_ROUTE" { - corrections = append(corrections, &models.Correction{ - Msg: d.String(), - F: func() error { return c.deleteWorkerRoute(ex.Original.(cloudflare.WorkerRoute).ID, domainID) }, - }) - } else { - corr := c.deleteRec(ex.Original.(cloudflare.DNSRecord), domainID) - // DS records must always have a corresponding NS record. - // Therefore, we remove DS records before any NS records. - if d.Existing.Type == "DS" { - corrections = append([]*models.Correction{corr}, corrections...) - } else { - corrections = append(corrections, corr) - } - } - } - for _, d := range create { - des := d.Desired - if des.Type == "PAGE_RULE" { - corrections = append(corrections, &models.Correction{ - Msg: d.String(), - F: func() error { return c.createPageRule(domainID, des.GetTargetField()) }, - }) - } else if des.Type == "WORKER_ROUTE" { - corrections = append(corrections, &models.Correction{ - Msg: d.String(), - F: func() error { return c.createWorkerRoute(domainID, des.GetTargetField()) }, - }) - } else { - corr := c.createRec(des, domainID) - // DS records must always have a corresponding NS record. - // Therefore, we create NS records before any DS records. - if d.Desired.Type == "NS" { - corrections = append(corr, corrections...) - } else { - corrections = append(corrections, corr...) - } - } - } - - for _, d := range mod { - rec := d.Desired - ex := d.Existing - if rec.Type == "PAGE_RULE" { - corrections = append(corrections, &models.Correction{ - Msg: d.String(), - F: func() error { - return c.updatePageRule(ex.Original.(cloudflare.PageRule).ID, domainID, rec.GetTargetField()) - }, - }) - } else if rec.Type == "WORKER_ROUTE" { - corrections = append(corrections, &models.Correction{ - Msg: d.String(), - F: func() error { - return c.updateWorkerRoute(ex.Original.(cloudflare.WorkerRoute).ID, domainID, rec.GetTargetField()) - }, - }) - } else { - e := ex.Original.(cloudflare.DNSRecord) - proxy := e.Proxiable && rec.Metadata[metaProxy] != "off" - corrections = append(corrections, &models.Correction{ - Msg: d.String(), - F: func() error { return c.modifyRecord(domainID, e.ID, proxy, rec) }, - }) - } - } - - // Add universalSSL change to corrections when needed - if changed, newState, err := c.checkUniversalSSL(dc, domainID); err == nil && changed { - var newStateString string - if newState { - newStateString = "enabled" - } else { - newStateString = "disabled" - } - corrections = append(corrections, &models.Correction{ - Msg: fmt.Sprintf("Universal SSL will be %s for this domain.", newStateString), - F: func() error { return c.changeUniversalSSL(domainID, newState) }, - }) - } - - return corrections, nil - } // Cloudflare is a "ByRecord" API. instructions, err := diff2.ByRecord(records, dc, genComparable) diff --git a/providers/cloudns/cloudnsProvider.go b/providers/cloudns/cloudnsProvider.go index e24685a1b7..05471312a7 100644 --- a/providers/cloudns/cloudnsProvider.go +++ b/providers/cloudns/cloudnsProvider.go @@ -8,7 +8,6 @@ import ( "github.com/StackExchange/dnscontrol/v4/models" "github.com/StackExchange/dnscontrol/v4/pkg/diff" - "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/providers" "github.com/miekg/dns/dnsutil" ) @@ -130,13 +129,7 @@ func (c *cloudnsProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, exi } var corrections []*models.Correction - var differ diff.Differ - if !diff2.EnableDiff2 { - differ = diff.New(dc) - } else { - differ = diff.NewCompat(dc) - } - _, create, del, modify, err := differ.IncrementalDiff(existingRecords) + _, create, del, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords) if err != nil { return nil, err } diff --git a/providers/cscglobal/dns.go b/providers/cscglobal/dns.go index e00f1b3d1e..ba41ff72a5 100644 --- a/providers/cscglobal/dns.go +++ b/providers/cscglobal/dns.go @@ -5,7 +5,6 @@ import ( "github.com/StackExchange/dnscontrol/v4/models" "github.com/StackExchange/dnscontrol/v4/pkg/diff" - "github.com/StackExchange/dnscontrol/v4/pkg/diff2" ) // GetZoneRecords gets the records of a zone and returns them in RecordConfig format. @@ -80,14 +79,7 @@ func (client *providerClient) GetZoneRecordsCorrections(dc *models.DomainConfig, //txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records var corrections []*models.Correction - var err error - var differ diff.Differ - if !diff2.EnableDiff2 { - differ = diff.New(dc) - } else { - differ = diff.NewCompat(dc) - } - _, creates, dels, modifications, err := differ.IncrementalDiff(foundRecords) + _, creates, dels, modifications, err := diff.NewCompat(dc).IncrementalDiff(foundRecords) if err != nil { return nil, err } diff --git a/providers/desec/desecProvider.go b/providers/desec/desecProvider.go index 275a9a45e7..caf69526e6 100644 --- a/providers/desec/desecProvider.go +++ b/providers/desec/desecProvider.go @@ -8,7 +8,6 @@ import ( "github.com/StackExchange/dnscontrol/v4/models" "github.com/StackExchange/dnscontrol/v4/pkg/diff" - "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/pkg/printer" "github.com/StackExchange/dnscontrol/v4/pkg/txtutil" "github.com/StackExchange/dnscontrol/v4/providers" @@ -165,14 +164,7 @@ func (c *desecProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, exist PrepDesiredRecords(dc, minTTL) var corrections []*models.Correction - var err error - var keysToUpdate map[models.RecordKey][]string - if !diff2.EnableDiff2 { - // diff existing vs. current. - keysToUpdate, err = (diff.New(dc)).ChangedGroups(existing) - } else { - keysToUpdate, err = (diff.NewCompat(dc)).ChangedGroups(existing) - } + keysToUpdate, err := diff.NewCompat(dc).ChangedGroups(existing) if err != nil { return nil, err } diff --git a/providers/digitalocean/digitaloceanProvider.go b/providers/digitalocean/digitaloceanProvider.go index 6bcf8b1061..9ba96a3a3b 100644 --- a/providers/digitalocean/digitaloceanProvider.go +++ b/providers/digitalocean/digitaloceanProvider.go @@ -10,7 +10,6 @@ import ( "github.com/StackExchange/dnscontrol/v4/models" "github.com/StackExchange/dnscontrol/v4/pkg/diff" - "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/pkg/txtutil" "github.com/StackExchange/dnscontrol/v4/providers" "github.com/digitalocean/godo" @@ -174,13 +173,7 @@ func (api *digitaloceanProvider) GetZoneRecordsCorrections(dc *models.DomainConf ctx := context.Background() var corrections []*models.Correction - var differ diff.Differ - if !diff2.EnableDiff2 { - differ = diff.New(dc) - } else { - differ = diff.NewCompat(dc) - } - _, toCreate, toDelete, toModify, err := differ.IncrementalDiff(existingRecords) + _, toCreate, toDelete, toModify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords) if err != nil { return nil, err } diff --git a/providers/dnsimple/dnsimpleProvider.go b/providers/dnsimple/dnsimpleProvider.go index fb5d269237..3789792afd 100644 --- a/providers/dnsimple/dnsimpleProvider.go +++ b/providers/dnsimple/dnsimpleProvider.go @@ -11,7 +11,6 @@ import ( "github.com/StackExchange/dnscontrol/v4/models" "github.com/StackExchange/dnscontrol/v4/pkg/diff" - "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/pkg/printer" "github.com/StackExchange/dnscontrol/v4/providers" dnsimpleapi "github.com/dnsimple/dnsimple-go/dnsimple" @@ -150,13 +149,7 @@ func (c *dnsimpleProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, ac } corrections = append(corrections, dnssecFixes...) - var differ diff.Differ - if !diff2.EnableDiff2 { - differ = diff.New(dc) - } else { - differ = diff.NewCompat(dc) - } - _, create, del, modify, err := differ.IncrementalDiff(actual) + _, create, del, modify, err := diff.NewCompat(dc).IncrementalDiff(actual) if err != nil { return nil, err } diff --git a/providers/dnsmadeeasy/dnsMadeEasyProvider.go b/providers/dnsmadeeasy/dnsMadeEasyProvider.go index d6942b16d3..72afc913b8 100644 --- a/providers/dnsmadeeasy/dnsMadeEasyProvider.go +++ b/providers/dnsmadeeasy/dnsMadeEasyProvider.go @@ -8,7 +8,6 @@ import ( "github.com/StackExchange/dnscontrol/v4/models" "github.com/StackExchange/dnscontrol/v4/pkg/diff" - "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/pkg/txtutil" "github.com/StackExchange/dnscontrol/v4/providers" ) @@ -119,13 +118,7 @@ func (api *dnsMadeEasyProvider) GetZoneRecordsCorrections(dc *models.DomainConfi } var corrections []*models.Correction - var differ diff.Differ - if !diff2.EnableDiff2 { - differ = diff.New(dc) - } else { - differ = diff.NewCompat(dc) - } - _, create, del, modify, err := differ.IncrementalDiff(existingRecords) + _, create, del, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords) if err != nil { return nil, err } diff --git a/providers/domainnameshop/dns.go b/providers/domainnameshop/dns.go index 27acee61b0..436356456a 100644 --- a/providers/domainnameshop/dns.go +++ b/providers/domainnameshop/dns.go @@ -7,7 +7,6 @@ import ( "github.com/StackExchange/dnscontrol/v4/models" "github.com/StackExchange/dnscontrol/v4/pkg/diff" - "github.com/StackExchange/dnscontrol/v4/pkg/diff2" ) func (api *domainNameShopProvider) GetZoneRecords(domain string, meta map[string]string) (models.Records, error) { @@ -41,13 +40,7 @@ func (api *domainNameShopProvider) GetZoneRecordsCorrections(dc *models.DomainCo } var corrections []*models.Correction - var differ diff.Differ - if !diff2.EnableDiff2 { - differ = diff.New(dc) - } else { - differ = diff.NewCompat(dc) - } - _, create, delete, modify, err := differ.IncrementalDiff(existingRecords) + _, create, delete, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords) if err != nil { return nil, err } diff --git a/providers/exoscale/exoscaleProvider.go b/providers/exoscale/exoscaleProvider.go index ec303aa244..b689f5a148 100644 --- a/providers/exoscale/exoscaleProvider.go +++ b/providers/exoscale/exoscaleProvider.go @@ -12,7 +12,6 @@ import ( "github.com/StackExchange/dnscontrol/v4/models" "github.com/StackExchange/dnscontrol/v4/pkg/diff" - "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/pkg/printer" "github.com/StackExchange/dnscontrol/v4/providers" ) @@ -192,13 +191,7 @@ func (c *exoscaleProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, ex domainID := *domain.ID var corrections []*models.Correction - var differ diff.Differ - if !diff2.EnableDiff2 { - differ = diff.New(dc) - } else { - differ = diff.NewCompat(dc) - } - _, create, toDelete, modify, err := differ.IncrementalDiff(existingRecords) + _, create, toDelete, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords) if err != nil { return nil, err } diff --git a/providers/gandiv5/gandi_v5Provider.go b/providers/gandiv5/gandi_v5Provider.go index 6a069bd70d..02838bcfd0 100644 --- a/providers/gandiv5/gandi_v5Provider.go +++ b/providers/gandiv5/gandi_v5Provider.go @@ -22,7 +22,6 @@ import ( "strings" "github.com/StackExchange/dnscontrol/v4/models" - "github.com/StackExchange/dnscontrol/v4/pkg/diff" "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/pkg/printer" "github.com/StackExchange/dnscontrol/v4/pkg/txtutil" @@ -192,7 +191,7 @@ func PrepDesiredRecords(dc *models.DomainConfig) { } // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. -func (client *gandiv5Provider) GetZoneRecordsCorrections(dc *models.DomainConfig, existing models.Records) ([]*models.Correction, error) { +func (client *gandiv5Provider) GetZoneRecordsCorrections(dc *models.DomainConfig, existing models.Records) (corrections []*models.Correction, err error) { if client.debug { debugRecords("GenDC input", existing) } @@ -200,117 +199,6 @@ func (client *gandiv5Provider) GetZoneRecordsCorrections(dc *models.DomainConfig PrepDesiredRecords(dc) txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records - var corrections []*models.Correction - if !diff2.EnableDiff2 { - - // diff existing vs. current. - differ := diff.New(dc) - keysToUpdate, err := differ.ChangedGroups(existing) - if err != nil { - return nil, err - } - if client.debug { - diff.DebugKeyMapMap("GenDC diff", keysToUpdate) - } - if len(keysToUpdate) == 0 { - return nil, nil - } - - // Regroup data by FQDN. ChangedGroups returns data grouped by label:RType tuples. - affectedLabels, msgsForLabel := gatherAffectedLabels(keysToUpdate) - _, desiredRecords := dc.Records.GroupedByFQDN() - doesLabelExist := existing.FQDNMap() - - g := gandi.NewLiveDNSClient(config.Config{ - APIKey: client.apikey, - SharingID: client.sharingid, - Debug: client.debug, - }) - - // For any key with an update, delete or replace those records. - for label := range affectedLabels { - if len(desiredRecords[label]) == 0 { - // No records matching this key? This can only mean that all - // the records were deleted. Delete them. - - msgs := strings.Join(msgsForLabel[label], "\n") - domain := dc.Name - shortname := dnsutil.TrimDomainName(label, dc.Name) - corrections = append(corrections, - &models.Correction{ - Msg: msgs, - F: func() error { - err := g.DeleteDomainRecordsByName(domain, shortname) - if err != nil { - return err - } - return nil - }, - }) - - } else { - // Replace all the records at a label with our new records. - - // Generate the new data in Gandi's format. - ns := recordsToNative(desiredRecords[label], dc.Name) - - if doesLabelExist[label] { - // Records exist for this label. Replace them with what we have. - - msg := strings.Join(msgsForLabel[label], "\n") - domain := dc.Name - shortname := dnsutil.TrimDomainName(label, dc.Name) - corrections = append(corrections, - &models.Correction{ - Msg: msg, - F: func() error { - res, err := g.UpdateDomainRecordsByName(domain, shortname, ns) - if err != nil { - return fmt.Errorf("%+v: %w", res, err) - } - return nil - }, - }) - - } else { - // First time putting data on this label. Create it. - - // We have to create the label one rtype at a time. - ns := recordsToNative(desiredRecords[label], dc.Name) - for _, n := range ns { - msg := strings.Join(msgsForLabel[label], "\n") - domain := dc.Name - shortname := dnsutil.TrimDomainName(label, dc.Name) - rtype := n.RrsetType - ttl := n.RrsetTTL - values := n.RrsetValues - corrections = append(corrections, - &models.Correction{ - Msg: msg, - F: func() error { - res, err := g.CreateDomainRecord(domain, shortname, rtype, ttl, values) - if err != nil { - return fmt.Errorf("%+v: %w", res, err) - } - return nil - }, - }) - } - } - } - } - - // NB(tlim): This sort is just to make updates look pretty. It is - // cosmetic. The risk here is that there may be some updates that - // require a specific order (for example a delete before an add). - // However the code doesn't seem to have such situation. All tests - // pass. That said, if this breaks anything, the easiest fix might - // be to just remove the sort. - sort.Slice(corrections, func(i, j int) bool { return diff.CorrectionLess(corrections, i, j) }) - - return corrections, nil - } - g := gandi.NewLiveDNSClient(config.Config{ APIKey: client.apikey, SharingID: client.sharingid, diff --git a/providers/gcloud/gcloudProvider.go b/providers/gcloud/gcloudProvider.go index e922200dd9..b81e0a9281 100644 --- a/providers/gcloud/gcloudProvider.go +++ b/providers/gcloud/gcloudProvider.go @@ -11,7 +11,6 @@ import ( "github.com/StackExchange/dnscontrol/v4/models" "github.com/StackExchange/dnscontrol/v4/pkg/diff" - "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/pkg/printer" "github.com/StackExchange/dnscontrol/v4/pkg/txtutil" "github.com/StackExchange/dnscontrol/v4/providers" @@ -273,13 +272,7 @@ func (g *gcloudProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, exis } // first collect keys that have changed - var differ diff.Differ - if !diff2.EnableDiff2 { - differ = diff.New(dc) - } else { - differ = diff.NewCompat(dc) - } - _, create, delete, modify, err := differ.IncrementalDiff(existingRecords) + _, create, delete, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords) if err != nil { return nil, fmt.Errorf("incdiff error: %w", err) } diff --git a/providers/gcore/gcoreProvider.go b/providers/gcore/gcoreProvider.go index f5ff49ad19..81d2fceb8e 100644 --- a/providers/gcore/gcoreProvider.go +++ b/providers/gcore/gcoreProvider.go @@ -7,7 +7,6 @@ import ( "strings" "github.com/StackExchange/dnscontrol/v4/models" - "github.com/StackExchange/dnscontrol/v4/pkg/diff" "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/providers" @@ -137,119 +136,46 @@ func (c *gcoreProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, exist var deletions []*models.Correction var reports []*models.Correction - if !diff2.EnableDiff2 { - - // diff existing vs. current. - differ := diff.New(dc) - keysToUpdate, err := differ.ChangedGroups(existing) - if err != nil { - return nil, err - } - if len(keysToUpdate) == 0 { - return nil, nil - } - - desiredRecords := dc.Records.GroupedByKey() - existingRecords := existing.GroupedByKey() - - for label := range keysToUpdate { - if _, ok := desiredRecords[label]; !ok { - // record deleted in update - // Copy all params to avoid overwrites - zone := dc.Name - name := label.NameFQDN - typ := label.Type - msg := generateChangeMsg(keysToUpdate[label]) - deletions = append(deletions, &models.Correction{ - Msg: msg, - F: func() error { - return c.provider.DeleteRRSet(c.ctx, zone, name, typ) - }, - }) - - } else if _, ok := existingRecords[label]; !ok { - // record created in update - record := recordsToNative(desiredRecords[label], label) - if record == nil { - panic("No records matching label") - } - - // Copy all params to avoid overwrites - zone := dc.Name - name := label.NameFQDN - typ := label.Type - msg := generateChangeMsg(keysToUpdate[label]) - corrections = append(corrections, &models.Correction{ - Msg: msg, - F: func() error { - return c.provider.CreateRRSet(c.ctx, zone, name, typ, *record) - }, - }) - - } else { - // record modified in update - record := recordsToNative(desiredRecords[label], label) - if record == nil { - panic("No records matching label") - } - - // Copy all params to avoid overwrites - zone := dc.Name - name := label.NameFQDN - typ := label.Type - msg := generateChangeMsg(keysToUpdate[label]) - corrections = append(corrections, &models.Correction{ - Msg: msg, - F: func() error { - return c.provider.UpdateRRSet(c.ctx, zone, name, typ, *record) - }, - }) - } - } - - } else { - // Diff2 version - changes, err := diff2.ByRecordSet(existing, dc, nil) - if err != nil { - return nil, err - } - - for _, change := range changes { - record := recordsToNative(change.New, change.Key) - - // Copy all params to avoid overwrites - zone := dc.Name - name := change.Key.NameFQDN - typ := change.Key.Type - msg := generateChangeMsg(change.Msgs) + changes, err := diff2.ByRecordSet(existing, dc, nil) + if err != nil { + return nil, err + } - switch change.Type { - case diff2.REPORT: - corrections = append(corrections, &models.Correction{Msg: change.MsgsJoined}) - case diff2.CREATE: - corrections = append(corrections, &models.Correction{ - Msg: msg, - F: func() error { - return c.provider.CreateRRSet(c.ctx, zone, name, typ, *record) - }, - }) - case diff2.CHANGE: - corrections = append(corrections, &models.Correction{ - Msg: msg, - F: func() error { - return c.provider.UpdateRRSet(c.ctx, zone, name, typ, *record) - }, - }) - case diff2.DELETE: - deletions = append(deletions, &models.Correction{ - Msg: msg, - F: func() error { - return c.provider.DeleteRRSet(c.ctx, zone, name, typ) - }, - }) - default: - panic(fmt.Sprintf("unhandled change.Type %s", change.Type)) - } + for _, change := range changes { + record := recordsToNative(change.New, change.Key) + + // Copy all params to avoid overwrites + zone := dc.Name + name := change.Key.NameFQDN + typ := change.Key.Type + msg := generateChangeMsg(change.Msgs) + + switch change.Type { + case diff2.REPORT: + corrections = append(corrections, &models.Correction{Msg: change.MsgsJoined}) + case diff2.CREATE: + corrections = append(corrections, &models.Correction{ + Msg: msg, + F: func() error { + return c.provider.CreateRRSet(c.ctx, zone, name, typ, *record) + }, + }) + case diff2.CHANGE: + corrections = append(corrections, &models.Correction{ + Msg: msg, + F: func() error { + return c.provider.UpdateRRSet(c.ctx, zone, name, typ, *record) + }, + }) + case diff2.DELETE: + deletions = append(deletions, &models.Correction{ + Msg: msg, + F: func() error { + return c.provider.DeleteRRSet(c.ctx, zone, name, typ) + }, + }) + default: + panic(fmt.Sprintf("unhandled change.Type %s", change.Type)) } } diff --git a/providers/hedns/hednsProvider.go b/providers/hedns/hednsProvider.go index 13aaa37460..0ee6d14855 100644 --- a/providers/hedns/hednsProvider.go +++ b/providers/hedns/hednsProvider.go @@ -16,7 +16,6 @@ import ( "github.com/PuerkitoBio/goquery" "github.com/StackExchange/dnscontrol/v4/models" - "github.com/StackExchange/dnscontrol/v4/pkg/diff" "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/pkg/txtutil" "github.com/StackExchange/dnscontrol/v4/providers" @@ -195,50 +194,9 @@ func (c *hednsProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, recor // Normalize txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records - // Fallback to legacy mode if diff2 is not enabled, remove when diff1 is deprecated. - if !diff2.EnableDiff2 { - return c.getDiff1DomainCorrections(dc, zoneID, prunedRecords) - } return c.getDiff2DomainCorrections(dc, zoneID, prunedRecords) } -func (c *hednsProvider) getDiff1DomainCorrections(dc *models.DomainConfig, zoneID uint64, records models.Records) ([]*models.Correction, error) { - var corrections []*models.Correction - - differ := diff.New(dc) - _, toCreate, toDelete, toModify, err := differ.IncrementalDiff(records) - if err != nil { - return nil, err - } - - for _, del := range toDelete { - recordID := del.Existing.Original.(Record).RecordID - corrections = append(corrections, &models.Correction{ - Msg: del.String(), - F: func() error { return c.deleteZoneRecord(zoneID, recordID) }, - }) - } - - for _, cre := range toCreate { - record := cre.Desired - corrections = append(corrections, &models.Correction{ - Msg: cre.String(), - F: func() error { return c.createZoneRecord(zoneID, record) }, - }) - } - - for _, mod := range toModify { - record := mod.Desired - recordID := mod.Existing.Original.(Record).RecordID - corrections = append(corrections, &models.Correction{ - Msg: mod.String(), - F: func() error { return c.changeZoneRecord(zoneID, recordID, record) }, - }) - } - - return corrections, nil -} - func (c *hednsProvider) getDiff2DomainCorrections(dc *models.DomainConfig, zoneID uint64, records models.Records) ([]*models.Correction, error) { changes, err := diff2.ByRecord(records, dc, nil) if err != nil { diff --git a/providers/hetzner/hetznerProvider.go b/providers/hetzner/hetznerProvider.go index a04515cd14..ec85c1ecc2 100644 --- a/providers/hetzner/hetznerProvider.go +++ b/providers/hetzner/hetznerProvider.go @@ -7,7 +7,6 @@ import ( "github.com/StackExchange/dnscontrol/v4/models" "github.com/StackExchange/dnscontrol/v4/pkg/diff" - "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/pkg/txtutil" "github.com/StackExchange/dnscontrol/v4/providers" ) @@ -70,19 +69,12 @@ func (api *hetznerProvider) EnsureZoneExists(domain string) error { } // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. -func (api *hetznerProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, error) { +func (api *hetznerProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) (corrections []*models.Correction, err error) { domain := dc.Name txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records - var corrections []*models.Correction - var differ diff.Differ - if !diff2.EnableDiff2 { - differ = diff.New(dc) - } else { - differ = diff.NewCompat(dc) - } - _, create, del, modify, err := differ.IncrementalDiff(existingRecords) + _, create, del, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords) if err != nil { return nil, err } diff --git a/providers/hexonet/records.go b/providers/hexonet/records.go index 9102f3990b..ea8a90f103 100644 --- a/providers/hexonet/records.go +++ b/providers/hexonet/records.go @@ -9,7 +9,6 @@ import ( "github.com/StackExchange/dnscontrol/v4/models" "github.com/StackExchange/dnscontrol/v4/pkg/diff" - "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/pkg/txtutil" ) @@ -58,25 +57,11 @@ func (n *HXClient) GetZoneRecords(domain string, meta map[string]string) (models } // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. -func (n *HXClient) GetZoneRecordsCorrections(dc *models.DomainConfig, actual models.Records) ([]*models.Correction, error) { - - // actual, err := n.GetZoneRecords(dc.Name) - // if err != nil { - // return nil, err - // } - - //checkNSModifications(dc) +func (n *HXClient) GetZoneRecordsCorrections(dc *models.DomainConfig, actual models.Records) (corrections []*models.Correction, err error) { txtutil.SplitSingleLongTxt(dc.Records) - var corrections []*models.Correction - var differ diff.Differ - if !diff2.EnableDiff2 { - differ = diff.New(dc) - } else { - differ = diff.NewCompat(dc) - } - _, create, del, mod, err := differ.IncrementalDiff(actual) + _, create, del, mod, err := diff.NewCompat(dc).IncrementalDiff(actual) if err != nil { return nil, err } diff --git a/providers/hostingde/hostingdeProvider.go b/providers/hostingde/hostingdeProvider.go index be91b2413e..93a332e513 100644 --- a/providers/hostingde/hostingdeProvider.go +++ b/providers/hostingde/hostingdeProvider.go @@ -10,7 +10,6 @@ import ( "github.com/StackExchange/dnscontrol/v4/models" "github.com/StackExchange/dnscontrol/v4/pkg/diff" - "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/providers" ) @@ -138,12 +137,7 @@ func (hp *hostingdeProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, return nil, err } - var create, del, mod diff.Changeset - if !diff2.EnableDiff2 { - _, create, del, mod, err = diff.New(dc).IncrementalDiff(records) - } else { - _, create, del, mod, err = diff.NewCompat(dc).IncrementalDiff(records) - } + _, create, del, mod, err := diff.NewCompat(dc).IncrementalDiff(records) if err != nil { return nil, err } diff --git a/providers/inwx/inwxProvider.go b/providers/inwx/inwxProvider.go index 6768dd24f1..cb78c74999 100644 --- a/providers/inwx/inwxProvider.go +++ b/providers/inwx/inwxProvider.go @@ -9,7 +9,6 @@ import ( "github.com/StackExchange/dnscontrol/v4/models" "github.com/StackExchange/dnscontrol/v4/pkg/diff" - "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/pkg/printer" "github.com/StackExchange/dnscontrol/v4/pkg/txtutil" "github.com/StackExchange/dnscontrol/v4/providers" @@ -237,13 +236,7 @@ func (api *inwxAPI) GetZoneRecordsCorrections(dc *models.DomainConfig, foundReco } var corrections []*models.Correction - var differ diff.Differ - if !diff2.EnableDiff2 { - differ = diff.New(dc) - } else { - differ = diff.NewCompat(dc) - } - _, create, del, mod, err := differ.IncrementalDiff(foundRecords) + _, create, del, mod, err := diff.NewCompat(dc).IncrementalDiff(foundRecords) if err != nil { return nil, err } diff --git a/providers/linode/linodeProvider.go b/providers/linode/linodeProvider.go index 1e68dfc999..62711815dd 100644 --- a/providers/linode/linodeProvider.go +++ b/providers/linode/linodeProvider.go @@ -11,7 +11,6 @@ import ( "github.com/StackExchange/dnscontrol/v4/models" "github.com/StackExchange/dnscontrol/v4/pkg/diff" - "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/providers" "github.com/miekg/dns/dnsutil" "golang.org/x/oauth2" @@ -126,7 +125,7 @@ func (api *linodeProvider) GetZoneRecords(domain string, meta map[string]string) } // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. -func (api *linodeProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, error) { +func (api *linodeProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) (corrections []*models.Correction, err error) { // Linode doesn't allow selecting an arbitrary TTL, only a set of predefined values // We need to make sure we don't change it every time if it is as close as it's going to get // The documentation says that it will always round up to the next highest value: 300 -> 300, 301 -> 3600. @@ -135,7 +134,6 @@ func (api *linodeProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, ex record.TTL = fixTTL(record.TTL) } - var err error if api.domainIndex == nil { if err = api.fetchDomainList(); err != nil { return nil, err @@ -146,14 +144,7 @@ func (api *linodeProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, ex return nil, fmt.Errorf("'%s' not a zone in Linode account", dc.Name) } - var corrections []*models.Correction - var differ diff.Differ - if !diff2.EnableDiff2 { - differ = diff.New(dc) - } else { - differ = diff.NewCompat(dc) - } - _, create, del, modify, err := differ.IncrementalDiff(existingRecords) + _, create, del, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords) if err != nil { return nil, err } diff --git a/providers/loopia/loopiaProvider.go b/providers/loopia/loopiaProvider.go index 3e0740b3e5..3a504f0892 100644 --- a/providers/loopia/loopiaProvider.go +++ b/providers/loopia/loopiaProvider.go @@ -24,7 +24,6 @@ import ( "github.com/StackExchange/dnscontrol/v4/models" "github.com/StackExchange/dnscontrol/v4/pkg/diff" - "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/pkg/printer" "github.com/StackExchange/dnscontrol/v4/pkg/txtutil" "github.com/StackExchange/dnscontrol/v4/providers" @@ -266,7 +265,7 @@ func gatherAffectedLabels(groups map[models.RecordKey][]string) (labels map[stri } // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. -func (c *APIClient) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, error) { +func (c *APIClient) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) (corrections []*models.Correction, err error) { if c.Debug { debugRecords("GenerateZoneRecordsCorrections input:\n", existingRecords) } @@ -275,14 +274,8 @@ func (c *APIClient) GetZoneRecordsCorrections(dc *models.DomainConfig, existingR txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records PrepDesiredRecords(dc) - var corrections []*models.Correction var keysToUpdate map[models.RecordKey][]string - var differ diff.Differ - if !diff2.EnableDiff2 { - differ = diff.New(dc) - } else { - differ = diff.NewCompat(dc) - } + differ := diff.NewCompat(dc) _, create, del, modify, err := differ.IncrementalDiff(existingRecords) if err != nil { return nil, err diff --git a/providers/luadns/luadnsProvider.go b/providers/luadns/luadnsProvider.go index 7aed027631..f08d333623 100644 --- a/providers/luadns/luadnsProvider.go +++ b/providers/luadns/luadnsProvider.go @@ -5,7 +5,6 @@ import ( "fmt" "github.com/StackExchange/dnscontrol/v4/models" - "github.com/StackExchange/dnscontrol/v4/pkg/diff" "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/providers" ) @@ -95,7 +94,7 @@ func (l *luadnsProvider) GetZoneRecords(domain string, meta map[string]string) ( } // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. -func (l *luadnsProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, records models.Records) ([]*models.Correction, error) { +func (l *luadnsProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, records models.Records) (corrections []*models.Correction, err error) { checkNS(dc) @@ -104,33 +103,7 @@ func (l *luadnsProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, reco return nil, err } - var corrections []*models.Correction var corrs []*models.Correction - if !diff2.EnableDiff2 { - differ := diff.New(dc) - _, create, del, mod, err := differ.IncrementalDiff(records) - if err != nil { - return nil, err - } - - corrections := []*models.Correction{} - for _, d := range del { - corrs := l.makeDeleteCorrection(d.Existing, domainID, d.String()) - corrections = append(corrections, corrs...) - } - - for _, d := range create { - corrs := l.makeCreateCorrection(d.Desired, domainID, d.String()) - corrections = append(corrections, corrs...) - } - - for _, d := range mod { - corrs := l.makeChangeCorrection(d.Existing, d.Desired, domainID, d.String()) - corrections = append(corrections, corrs...) - } - - return corrections, nil - } changes, err := diff2.ByRecord(records, dc, nil) if err != nil { diff --git a/providers/msdns/corrections.go b/providers/msdns/corrections.go index dc9ed5ff50..71ce9ba793 100644 --- a/providers/msdns/corrections.go +++ b/providers/msdns/corrections.go @@ -4,39 +4,17 @@ import ( "fmt" "github.com/StackExchange/dnscontrol/v4/models" - "github.com/StackExchange/dnscontrol/v4/pkg/diff" "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/pkg/txtutil" ) // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. -func (client *msdnsProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, foundRecords models.Records) ([]*models.Correction, error) { +func (client *msdnsProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, foundRecords models.Records) (corrections []*models.Correction, err error) { // Normalize models.PostProcessRecords(foundRecords) txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records - var corrections []*models.Correction - if !diff2.EnableDiff2 { - differ := diff.New(dc) - _, creates, dels, modifications, err := differ.IncrementalDiff(foundRecords) - if err != nil { - return nil, err - } - - // Generate changes. - for _, del := range dels { - corrections = append(corrections, client.deleteRec(client.dnsserver, dc.Name, del)) - } - for _, cre := range creates { - corrections = append(corrections, client.createRec(client.dnsserver, dc.Name, cre)...) - } - for _, m := range modifications { - corrections = append(corrections, client.modifyRec(client.dnsserver, dc.Name, m)) - } - return corrections, nil - } - changes, err := diff2.ByRecord(foundRecords, dc, nil) if err != nil { return nil, err @@ -108,34 +86,3 @@ func (client *msdnsProvider) modifyOneRecord(dnsserver, zonename string, oldrec, func (client *msdnsProvider) modifyRecordTTL(dnsserver, zonename string, oldrec, newrec *models.RecordConfig) error { return client.shell.RecordModifyTTL(dnsserver, zonename, oldrec, newrec.TTL) } - -func (client *msdnsProvider) deleteRec(dnsserver, domainname string, cor diff.Correlation) *models.Correction { - rec := cor.Existing - return &models.Correction{ - Msg: cor.String(), - F: func() error { - return client.shell.RecordDelete(dnsserver, domainname, rec) - }, - } -} - -func (client *msdnsProvider) createRec(dnsserver, domainname string, cre diff.Correlation) []*models.Correction { - rec := cre.Desired - arr := []*models.Correction{{ - Msg: cre.String(), - F: func() error { - return client.shell.RecordCreate(dnsserver, domainname, rec) - }, - }} - return arr -} - -func (client *msdnsProvider) modifyRec(dnsserver, domainname string, m diff.Correlation) *models.Correction { - old, rec := m.Existing, m.Desired - return &models.Correction{ - Msg: m.String(), - F: func() error { - return client.shell.RecordModify(dnsserver, domainname, old, rec) - }, - } -} diff --git a/providers/namecheap/namecheapProvider.go b/providers/namecheap/namecheapProvider.go index 31af80c94b..d7f23ae9c9 100644 --- a/providers/namecheap/namecheapProvider.go +++ b/providers/namecheap/namecheapProvider.go @@ -9,7 +9,6 @@ import ( "github.com/StackExchange/dnscontrol/v4/models" "github.com/StackExchange/dnscontrol/v4/pkg/diff" - "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/pkg/printer" "github.com/StackExchange/dnscontrol/v4/providers" nc "github.com/billputer/go-namecheap" @@ -245,13 +244,7 @@ func (n *namecheapProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, a return true }) - var differ diff.Differ - if !diff2.EnableDiff2 { - differ = diff.New(dc) - } else { - differ = diff.NewCompat(dc) - } - _, create, delete, modify, err := differ.IncrementalDiff(actual) + _, create, delete, modify, err := diff.NewCompat(dc).IncrementalDiff(actual) if err != nil { return nil, err } diff --git a/providers/namedotcom/records.go b/providers/namedotcom/records.go index 0dba6abb2a..10f42d4867 100644 --- a/providers/namedotcom/records.go +++ b/providers/namedotcom/records.go @@ -8,7 +8,6 @@ import ( "github.com/StackExchange/dnscontrol/v4/models" "github.com/StackExchange/dnscontrol/v4/pkg/diff" - "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/namedotcom/go/namecom" ) @@ -28,7 +27,7 @@ func (n *namedotcomProvider) GetZoneRecords(domain string, meta map[string]strin } // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. -func (n *namedotcomProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, actual models.Records) ([]*models.Correction, error) { +func (n *namedotcomProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, actual models.Records) (corrections []*models.Correction, err error) { checkNSModifications(dc) @@ -38,14 +37,7 @@ func (n *namedotcomProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, } } - var corrections []*models.Correction - var differ diff.Differ - if !diff2.EnableDiff2 { - differ = diff.New(dc) - } else { - differ = diff.NewCompat(dc) - } - _, create, del, mod, err := differ.IncrementalDiff(actual) + _, create, del, mod, err := diff.NewCompat(dc).IncrementalDiff(actual) if err != nil { return nil, err } diff --git a/providers/netcup/netcupProvider.go b/providers/netcup/netcupProvider.go index a2d51581be..936e09efcb 100644 --- a/providers/netcup/netcupProvider.go +++ b/providers/netcup/netcupProvider.go @@ -6,7 +6,6 @@ import ( "github.com/StackExchange/dnscontrol/v4/models" "github.com/StackExchange/dnscontrol/v4/pkg/diff" - "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/providers" ) @@ -69,7 +68,7 @@ func (api *netcupProvider) GetNameservers(domain string) ([]*models.Nameserver, } // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. -func (api *netcupProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, error) { +func (api *netcupProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) (corrections []*models.Correction, err error) { domain := dc.Name // no need for txtutil.SplitSingleLongTxt in function GetDomainCorrections @@ -89,14 +88,7 @@ func (api *netcupProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, ex } dc.Records = newRecords - var corrections []*models.Correction - var differ diff.Differ - if !diff2.EnableDiff2 { - differ = diff.New(dc) - } else { - differ = diff.NewCompat(dc) - } - _, create, del, modify, err := differ.IncrementalDiff(existingRecords) + _, create, del, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords) if err != nil { return nil, err } diff --git a/providers/netlify/netlifyProvider.go b/providers/netlify/netlifyProvider.go index 6b8b050fb8..eb7a4f0433 100644 --- a/providers/netlify/netlifyProvider.go +++ b/providers/netlify/netlifyProvider.go @@ -7,7 +7,6 @@ import ( "github.com/StackExchange/dnscontrol/v4/models" "github.com/StackExchange/dnscontrol/v4/pkg/diff" - "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/providers" "github.com/miekg/dns" ) @@ -140,15 +139,8 @@ func (n *netlifyProvider) GetZoneRecords(domain string, meta map[string]string) } // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. -func (n *netlifyProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, records models.Records) ([]*models.Correction, error) { - var corrections []*models.Correction - var differ diff.Differ - if !diff2.EnableDiff2 { - differ = diff.New(dc) - } else { - differ = diff.NewCompat(dc) - } - _, create, del, modify, err := differ.IncrementalDiff(records) +func (n *netlifyProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, records models.Records) (corrections []*models.Correction, err error) { + _, create, del, modify, err := diff.NewCompat(dc).IncrementalDiff(records) if err != nil { return nil, err } diff --git a/providers/ns1/ns1Provider.go b/providers/ns1/ns1Provider.go index b642e0c7e7..47307421ef 100644 --- a/providers/ns1/ns1Provider.go +++ b/providers/ns1/ns1Provider.go @@ -8,7 +8,6 @@ import ( "strings" "github.com/StackExchange/dnscontrol/v4/models" - "github.com/StackExchange/dnscontrol/v4/pkg/diff" "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/providers" "gopkg.in/ns1/ns1-go.v2/rest" @@ -187,8 +186,7 @@ func (n *nsone) getDomainCorrectionsDNSSEC(domain, toggleDNSSEC string) *models. } // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. -func (n *nsone) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, error) { - corrections := []*models.Correction{} +func (n *nsone) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) (corrections []*models.Correction, err error) { domain := dc.Name // add DNSSEC-related corrections @@ -196,49 +194,6 @@ func (n *nsone) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecor corrections = append(corrections, dnssecCorrections) } - if !diff2.EnableDiff2 { - - existingGrouped := existingRecords.GroupedByKey() - desiredGrouped := dc.Records.GroupedByKey() - - differ := diff.New(dc) - changedGroups, err := differ.ChangedGroups(existingRecords) - if err != nil { - return nil, err - } - - // each name/type is given to the api as a unit. - for k, descs := range changedGroups { - key := k - - desc := strings.Join(descs, "\n") - - _, current := existingGrouped[k] - recs, wanted := desiredGrouped[k] - - if wanted && !current { - // pure addition - corrections = append(corrections, &models.Correction{ - Msg: desc, - F: func() error { return n.add(recs, dc.Name) }, - }) - } else if current && !wanted { - // pure deletion - corrections = append(corrections, &models.Correction{ - Msg: desc, - F: func() error { return n.remove(key, dc.Name) }, - }) - } else { - // modification - corrections = append(corrections, &models.Correction{ - Msg: desc, - F: func() error { return n.modify(recs, dc.Name) }, - }) - } - } - return corrections, nil - } - changes, err := diff2.ByRecordSet(existingRecords, dc, nil) if err != nil { return nil, err diff --git a/providers/oracle/oracleProvider.go b/providers/oracle/oracleProvider.go index 8b269b710c..a9b0c078d5 100644 --- a/providers/oracle/oracleProvider.go +++ b/providers/oracle/oracleProvider.go @@ -8,7 +8,6 @@ import ( "github.com/StackExchange/dnscontrol/v4/models" "github.com/StackExchange/dnscontrol/v4/pkg/diff" - "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/pkg/printer" "github.com/StackExchange/dnscontrol/v4/pkg/txtutil" "github.com/StackExchange/dnscontrol/v4/providers" @@ -225,13 +224,7 @@ func (o *oracleProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, exis } } - var differ diff.Differ - if !diff2.EnableDiff2 { - differ = diff.New(dc) - } else { - differ = diff.NewCompat(dc) - } - _, create, dels, modify, err := differ.IncrementalDiff(existingRecords) + _, create, dels, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords) if err != nil { return nil, err } diff --git a/providers/ovh/ovhProvider.go b/providers/ovh/ovhProvider.go index ba18f800aa..d3db32430b 100644 --- a/providers/ovh/ovhProvider.go +++ b/providers/ovh/ovhProvider.go @@ -7,7 +7,6 @@ import ( "strings" "github.com/StackExchange/dnscontrol/v4/models" - "github.com/StackExchange/dnscontrol/v4/pkg/diff" "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/providers" "github.com/ovh/go-ovh/ovh" @@ -122,14 +121,7 @@ func (c *ovhProvider) GetZoneRecords(domain string, meta map[string]string) (mod // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. func (c *ovhProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, actual models.Records) ([]*models.Correction, error) { - var corrections []*models.Correction - var err error - if !diff2.EnableDiff2 { - corrections, err = c.getDiff1DomainCorrections(dc, actual) - } else { - corrections, err = c.getDiff2DomainCorrections(dc, actual) - } - + corrections, err := c.getDiff2DomainCorrections(dc, actual) if err != nil { return nil, err } @@ -146,42 +138,6 @@ func (c *ovhProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, actual return corrections, nil } -func (c *ovhProvider) getDiff1DomainCorrections(dc *models.DomainConfig, actual models.Records) ([]*models.Correction, error) { - var corrections []*models.Correction - - differ := diff.New(dc) - _, create, delete, modify, err := differ.IncrementalDiff(actual) - if err != nil { - return nil, err - } - - for _, del := range delete { - rec := del.Existing.Original.(*Record) - corrections = append(corrections, &models.Correction{ - Msg: del.String(), - F: c.deleteRecordFunc(rec.ID, dc.Name), - }) - } - - for _, cre := range create { - rec := cre.Desired - corrections = append(corrections, &models.Correction{ - Msg: cre.String(), - F: c.createRecordFunc(rec, dc.Name), - }) - } - - for _, mod := range modify { - oldR := mod.Existing.Original.(*Record) - newR := mod.Desired - corrections = append(corrections, &models.Correction{ - Msg: mod.String(), - F: c.updateRecordFunc(oldR, newR, dc.Name), - }) - } - return corrections, nil -} - func (c *ovhProvider) getDiff2DomainCorrections(dc *models.DomainConfig, actual models.Records) ([]*models.Correction, error) { var corrections []*models.Correction instructions, err := diff2.ByRecord(actual, dc, nil) diff --git a/providers/packetframe/packetframeProvider.go b/providers/packetframe/packetframeProvider.go index 2c1fff871f..cd9b4bde17 100644 --- a/providers/packetframe/packetframeProvider.go +++ b/providers/packetframe/packetframeProvider.go @@ -10,7 +10,6 @@ import ( "github.com/StackExchange/dnscontrol/v4/models" "github.com/StackExchange/dnscontrol/v4/pkg/diff" - "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/providers" ) @@ -101,20 +100,13 @@ func (api *packetframeProvider) GetZoneRecords(domain string, meta map[string]st } // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. -func (api *packetframeProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, error) { +func (api *packetframeProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) (corrections []*models.Correction, err error) { zone, err := api.getZone(dc.Name) if err != nil { return nil, fmt.Errorf("no such zone %q in Packetframe account", dc.Name) } - var corrections []*models.Correction - var differ diff.Differ - if !diff2.EnableDiff2 { - differ = diff.New(dc) - } else { - differ = diff.NewCompat(dc) - } - _, create, dels, modify, err := differ.IncrementalDiff(existingRecords) + _, create, dels, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords) if err != nil { return nil, err } diff --git a/providers/porkbun/porkbunProvider.go b/providers/porkbun/porkbunProvider.go index 0eddd1c9ff..ec2327d9f2 100644 --- a/providers/porkbun/porkbunProvider.go +++ b/providers/porkbun/porkbunProvider.go @@ -8,7 +8,6 @@ import ( "strings" "github.com/StackExchange/dnscontrol/v4/models" - "github.com/StackExchange/dnscontrol/v4/pkg/diff" "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/pkg/printer" "github.com/StackExchange/dnscontrol/v4/providers" @@ -86,7 +85,7 @@ func (c *porkbunProvider) GetNameservers(domain string) ([]*models.Nameserver, e } // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. -func (c *porkbunProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, error) { +func (c *porkbunProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) (corrections []*models.Correction, err error) { // Block changes to NS records for base domain checkNSModifications(dc) @@ -96,61 +95,6 @@ func (c *porkbunProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, exi record.TTL = fixTTL(record.TTL) } - var corrections []*models.Correction - if !diff2.EnableDiff2 { - - differ := diff.New(dc) - _, create, del, modify, err := differ.IncrementalDiff(existingRecords) - if err != nil { - return nil, err - } - - // Deletes first so changing type works etc. - for _, m := range del { - id := m.Existing.Original.(*domainRecord).ID - corr := &models.Correction{ - Msg: fmt.Sprintf("%s, porkbun ID: %s", m.String(), id), - F: func() error { - return c.deleteRecord(dc.Name, id) - }, - } - corrections = append(corrections, corr) - } - - for _, m := range create { - req, err := toReq(m.Desired) - if err != nil { - return nil, err - } - - corr := &models.Correction{ - Msg: m.String(), - F: func() error { - return c.createRecord(dc.Name, req) - }, - } - corrections = append(corrections, corr) - } - - for _, m := range modify { - id := m.Existing.Original.(*domainRecord).ID - req, err := toReq(m.Desired) - if err != nil { - return nil, err - } - - corr := &models.Correction{ - Msg: fmt.Sprintf("%s, porkbun ID: %s", m.String(), id), - F: func() error { - return c.modifyRecord(dc.Name, id, req) - }, - } - corrections = append(corrections, corr) - } - - return corrections, nil - } - changes, err := diff2.ByRecord(existingRecords, dc, nil) if err != nil { return nil, err diff --git a/providers/powerdns/diff.go b/providers/powerdns/diff.go index 1d246251cb..a48ef55deb 100644 --- a/providers/powerdns/diff.go +++ b/providers/powerdns/diff.go @@ -3,71 +3,12 @@ package powerdns import ( "context" "fmt" + "github.com/StackExchange/dnscontrol/v4/models" - "github.com/StackExchange/dnscontrol/v4/pkg/diff" "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/mittwald/go-powerdns/apis/zones" - "strings" ) -func (dsp *powerdnsProvider) getDiff1DomainCorrections(dc *models.DomainConfig, existing models.Records) ([]*models.Correction, error) { - // create record diff by group - keysToUpdate, err := (diff.New(dc)).ChangedGroups(existing) - if err != nil { - return nil, err - } - desiredRecords := dc.Records.GroupedByKey() - - var cuCorrections []*models.Correction - var dCorrections []*models.Correction - - // add create/update and delete corrections separately - for label, msgs := range keysToUpdate { - labelName := canonical(label.NameFQDN) - labelType := label.Type - msgJoined := strings.Join(msgs, "\n ") - - if _, ok := desiredRecords[label]; !ok { - // no record found so delete it - dCorrections = append(dCorrections, &models.Correction{ - Msg: msgJoined, - F: func() error { - return dsp.client.Zones().RemoveRecordSetFromZone(context.Background(), dsp.ServerName, dc.Name, labelName, labelType) - }, - }) - } else { - // record found so create or update it - ttl := desiredRecords[label][0].TTL - var records []zones.Record - for _, recordContent := range desiredRecords[label] { - records = append(records, zones.Record{ - Content: recordContent.GetTargetCombined(), - }) - } - cuCorrections = append(cuCorrections, &models.Correction{ - Msg: msgJoined, - F: func() error { - return dsp.client.Zones().AddRecordSetToZone(context.Background(), dsp.ServerName, dc.Name, zones.ResourceRecordSet{ - Name: labelName, - Type: labelType, - TTL: int(ttl), - Records: records, - ChangeType: zones.ChangeTypeReplace, - }) - }, - }) - } - } - - // append corrections in the right order - // delete corrections must be run first to avoid correlations with existing RR - var corrections []*models.Correction - corrections = append(corrections, dCorrections...) - corrections = append(corrections, cuCorrections...) - - return corrections, nil -} - func (dsp *powerdnsProvider) getDiff2DomainCorrections(dc *models.DomainConfig, existing models.Records) ([]*models.Correction, error) { changes, err := diff2.ByRecordSet(existing, dc, nil) if err != nil { diff --git a/providers/powerdns/dns.go b/providers/powerdns/dns.go index 140f1c74c4..aeca212848 100644 --- a/providers/powerdns/dns.go +++ b/providers/powerdns/dns.go @@ -5,7 +5,6 @@ import ( "net/http" "github.com/StackExchange/dnscontrol/v4/models" - "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/mittwald/go-powerdns/apis/zones" "github.com/mittwald/go-powerdns/pdnshttp" ) @@ -47,15 +46,8 @@ func (dsp *powerdnsProvider) GetZoneRecords(domain string, meta map[string]strin // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. func (dsp *powerdnsProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existing models.Records) ([]*models.Correction, error) { - // create record diff by group - var err error - var corrections []*models.Correction - if !diff2.EnableDiff2 { - corrections, err = dsp.getDiff1DomainCorrections(dc, existing) - } else { - corrections, err = dsp.getDiff2DomainCorrections(dc, existing) - } + corrections, err := dsp.getDiff2DomainCorrections(dc, existing) if err != nil { return nil, err } diff --git a/providers/route53/route53Provider.go b/providers/route53/route53Provider.go index 2fe31a1611..cf540f01fc 100644 --- a/providers/route53/route53Provider.go +++ b/providers/route53/route53Provider.go @@ -12,7 +12,6 @@ import ( "unicode/utf8" "github.com/StackExchange/dnscontrol/v4/models" - "github.com/StackExchange/dnscontrol/v4/pkg/diff" "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/pkg/printer" "github.com/StackExchange/dnscontrol/v4/pkg/txtutil" @@ -294,198 +293,6 @@ func (r *route53Provider) GetZoneRecordsCorrections(dc *models.DomainConfig, exi } var corrections []*models.Correction - if !diff2.EnableDiff2 { - - zone, err := r.getZone(dc) - if err != nil { - return nil, err - } - - // diff - differ := diff.New(dc, getAliasMap) - namesToUpdate, err := differ.ChangedGroups(existingRecords) - if err != nil { - return nil, err - } - - if len(namesToUpdate) == 0 { - return nil, nil - } - - updates := map[models.RecordKey][]*models.RecordConfig{} - - // for each name we need to update, collect relevant records from our desired domain state - for k := range namesToUpdate { - updates[k] = nil - for _, rc := range dc.Records { - if rc.Key() == k { - updates[k] = append(updates[k], rc) - } - } - } - - // updateOrder is the order that the updates will happen. - // The order should be sorted by NameFQDN, then Type, with R53_ALIAS_* - // types sorted after all other types. R53_ALIAS_* needs to be last - // because they are order dependent (aliases must refer to labels - // that already exist). - var updateOrder []models.RecordKey - // Collect the keys - for k := range updates { - updateOrder = append(updateOrder, k) - } - // Sort themm - sort.Slice(updateOrder, func(i, j int) bool { - if updateOrder[i].Type == updateOrder[j].Type { - return updateOrder[i].NameFQDN < updateOrder[j].NameFQDN - } - - if strings.HasPrefix(updateOrder[i].Type, "R53_ALIAS_") { - return false - } - if strings.HasPrefix(updateOrder[j].Type, "R53_ALIAS_") { - return true - } - - if updateOrder[i].NameFQDN == updateOrder[j].NameFQDN { - return updateOrder[i].Type < updateOrder[j].Type - } - return updateOrder[i].NameFQDN < updateOrder[j].NameFQDN - }) - - // we collect all changes into one of two categories now: - // pure deletions where we delete an entire record set, - // or changes where we upsert an entire record set. - dels := []r53Types.Change{} - delDesc := []string{} - changes := []r53Types.Change{} - changeDesc := []string{} - - for _, currentKey := range updateOrder { - recs := updates[currentKey] - // If there are no records in our desired state for a key, this - // indicates we should delete all records at that key. - if len(recs) == 0 { - // To delete, we submit the original resource set we got from r53. - var ( - rrset r53Types.ResourceRecordSet - found bool - ) - // Find the original resource set: - for _, orec := range r.originalRecords { - if unescape(orec.Name) == currentKey.NameFQDN && (string(orec.Type) == currentKey.Type || currentKey.Type == "R53_ALIAS_"+string(orec.Type)) { - rrset = orec - found = true - break - } - } - if !found { - // This should not happen. - return nil, fmt.Errorf("no record set found to delete. Name: '%s'. Type: '%s'", currentKey.NameFQDN, currentKey.Type) - } - // Assemble the change and add it to the list: - chg := r53Types.Change{ - Action: r53Types.ChangeActionDelete, - ResourceRecordSet: &rrset, - } - dels = append(dels, chg) - delDesc = append(delDesc, strings.Join(namesToUpdate[currentKey], "\n")) - } else { - // If it isn't a delete, it must be either a change or create. In - // either case, we build a new record set from the desired state and - // UPSERT it. - - if strings.HasPrefix(currentKey.Type, "R53_ALIAS_") { - // Each R53_ALIAS_* requires an individual change. - if len(recs) != 1 { - log.Fatal("Only one R53_ALIAS_ permitted on a label") - } - for _, rec := range recs { - rrset := aliasToRRSet(zone, rec) - rrset.Name = aws.String(currentKey.NameFQDN) - // Assemble the change and add it to the list: - chg := r53Types.Change{ - Action: r53Types.ChangeActionUpsert, - ResourceRecordSet: rrset, - } - changes = append(changes, chg) - changeDesc = append(changeDesc, strings.Join(namesToUpdate[currentKey], "\n")) - } - } else { - // All other keys combine their updates into one rrset: - rrset := &r53Types.ResourceRecordSet{ - Name: aws.String(currentKey.NameFQDN), - Type: r53Types.RRType(currentKey.Type), - } - for _, rec := range recs { - val := rec.GetTargetCombined() - rr := r53Types.ResourceRecord{ - Value: aws.String(val), - } - rrset.ResourceRecords = append(rrset.ResourceRecords, rr) - i := int64(rec.TTL) - rrset.TTL = &i // TODO: make sure that ttls are consistent within a set - } - // Assemble the change and add it to the list: - chg := r53Types.Change{ - Action: r53Types.ChangeActionUpsert, - ResourceRecordSet: rrset, - } - changes = append(changes, chg) - changeDesc = append(changeDesc, strings.Join(namesToUpdate[currentKey], "\n")) - } - - } - } - - addCorrection := func(msg string, req *r53.ChangeResourceRecordSetsInput) { - corrections = append(corrections, - &models.Correction{ - Msg: msg, - F: func() error { - var err error - req.HostedZoneId = zone.Id - withRetry(func() error { - _, err = r.client.ChangeResourceRecordSets(context.Background(), req) - return err - }) - return err - }, - }) - } - - batcher := newChangeBatcher(dels) - for batcher.Next() { - start, end := batcher.Batch() - batch := dels[start:end] - descBatchStr := "\n" + strings.Join(delDesc[start:end], "\n") + "\n" - req := &r53.ChangeResourceRecordSetsInput{ - ChangeBatch: &r53Types.ChangeBatch{Changes: batch}, - } - addCorrection(descBatchStr, req) - } - if err := batcher.Err(); err != nil { - return nil, err - } - - batcher = newChangeBatcher(changes) - for batcher.Next() { - start, end := batcher.Batch() - batch := changes[start:end] - descBatchStr := "\n" + strings.Join(changeDesc[start:end], "\n") + "\n" - req := &r53.ChangeResourceRecordSetsInput{ - ChangeBatch: &r53Types.ChangeBatch{Changes: batch}, - } - addCorrection(descBatchStr, req) - } - if err := batcher.Err(); err != nil { - return nil, err - } - - return corrections, nil - - } - changes := []r53Types.Change{} changeDesc := []string{} // TODO(tlim): This should be a [][]string so that we aren't joining strings until the last moment. diff --git a/providers/rwth/dns.go b/providers/rwth/dns.go index 45809f3fb0..645be5e5a4 100644 --- a/providers/rwth/dns.go +++ b/providers/rwth/dns.go @@ -5,7 +5,6 @@ import ( "github.com/StackExchange/dnscontrol/v4/models" "github.com/StackExchange/dnscontrol/v4/pkg/diff" - "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/pkg/txtutil" ) @@ -36,13 +35,7 @@ func (api *rwthProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, exis domain := dc.Name var corrections []*models.Correction - var differ diff.Differ - if !diff2.EnableDiff2 { - differ = diff.New(dc) - } else { - differ = diff.NewCompat(dc) - } - _, create, del, modify, err := differ.IncrementalDiff(existingRecords) + _, create, del, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords) if err != nil { return nil, err } diff --git a/providers/softlayer/softlayerProvider.go b/providers/softlayer/softlayerProvider.go index 3269d626de..326dc81b50 100644 --- a/providers/softlayer/softlayerProvider.go +++ b/providers/softlayer/softlayerProvider.go @@ -8,7 +8,6 @@ import ( "github.com/StackExchange/dnscontrol/v4/models" "github.com/StackExchange/dnscontrol/v4/pkg/diff" - "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/pkg/printer" "github.com/StackExchange/dnscontrol/v4/providers" "github.com/softlayer/softlayer-go/datatypes" @@ -77,18 +76,14 @@ func (s *softlayerProvider) GetZoneRecords(domainName string, meta map[string]st // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. func (s *softlayerProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, actual models.Records) ([]*models.Correction, error) { + var corrections []*models.Correction + domain, err := s.getDomain(&dc.Name) if err != nil { return nil, err } - var corrections []*models.Correction - var create, deletes, modify diff.Changeset - if !diff2.EnableDiff2 { - _, create, deletes, modify, err = diff.New(dc).IncrementalDiff(actual) - } else { - _, create, deletes, modify, err = diff.NewCompat(dc).IncrementalDiff(actual) - } + _, create, deletes, modify, err := diff.NewCompat(dc).IncrementalDiff(actual) if err != nil { return nil, err } diff --git a/providers/transip/transipProvider.go b/providers/transip/transipProvider.go index 6f76bcc930..aadb707824 100644 --- a/providers/transip/transipProvider.go +++ b/providers/transip/transipProvider.go @@ -7,7 +7,6 @@ import ( "strings" "github.com/StackExchange/dnscontrol/v4/models" - "github.com/StackExchange/dnscontrol/v4/pkg/diff" "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/providers" "github.com/transip/gotransip/v6" @@ -102,16 +101,13 @@ func (n *transipProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, cur removeOtherNS(dc) - if !diff2.EnableDiff2 { - corrections, err := n.getCorrectionsUsingOldDiff(dc, curRecords) - return corrections, err - } 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 @@ -243,76 +239,6 @@ func canDirectApplyDNSEntries(change diff2.Change) bool { 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, true) - if err != nil { - return nil, err - } - - 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, false) - if err != nil { - return nil, err - } - - 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, false) - if err != nil { - return nil, err - } - - // 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, true) - if err != nil { - return nil, err - } - - corrections = append(corrections, - &models.Correction{ - Msg: mod.String() + "[1/2]", - F: func() error { return n.domains.RemoveDNSEntry(dc.Name, oldEntry) }, - }, - &models.Correction{ - Msg: mod.String() + "[2/2]", - F: func() error { return n.domains.AddDNSEntry(dc.Name, targetEntry) }, - }, - ) - } - - } - - return corrections, nil -} - func canUpdateDNSEntry(desired *models.RecordConfig, existing *models.RecordConfig) bool { return desired.Name == existing.Name && desired.TTL == existing.TTL && desired.Type == existing.Type } diff --git a/providers/vultr/vultrProvider.go b/providers/vultr/vultrProvider.go index c493e41c99..1a5b5b5772 100644 --- a/providers/vultr/vultrProvider.go +++ b/providers/vultr/vultrProvider.go @@ -11,7 +11,6 @@ import ( "golang.org/x/oauth2" "github.com/StackExchange/dnscontrol/v4/models" - "github.com/StackExchange/dnscontrol/v4/pkg/diff" "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/providers" "github.com/vultr/govultr/v2" @@ -111,6 +110,7 @@ func (api *vultrProvider) GetZoneRecords(domain string, meta map[string]string) // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. func (api *vultrProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, curRecords models.Records) ([]*models.Correction, error) { + var corrections []*models.Correction for _, rec := range dc.Records { switch rec.Type { // #rtype_variations @@ -126,48 +126,6 @@ func (api *vultrProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, cur } } - var corrections []*models.Correction - if !diff2.EnableDiff2 { - differ := diff.New(dc) - _, create, toDelete, modify, err := differ.IncrementalDiff(curRecords) - - if err != nil { - return nil, err - } - - for _, mod := range toDelete { - id := mod.Existing.Original.(govultr.DomainRecord).ID - corrections = append(corrections, &models.Correction{ - Msg: fmt.Sprintf("%s; Vultr RecordID: %v", mod.String(), id), - F: func() error { - return api.client.DomainRecord.Delete(context.Background(), dc.Name, id) - }, - }) - } - - for _, mod := range modify { - r := toVultrRecord(dc, mod.Desired, mod.Existing.Original.(govultr.DomainRecord).ID) - corrections = append(corrections, &models.Correction{ - Msg: fmt.Sprintf("%s; Vultr RecordID: %v", mod.String(), r.ID), - F: func() error { - return api.client.DomainRecord.Update(context.Background(), dc.Name, r.ID, &govultr.DomainRecordReq{Name: r.Name, Type: r.Type, Data: r.Data, TTL: r.TTL, Priority: &r.Priority}) - }, - }) - } - - for _, mod := range create { - r := toVultrRecord(dc, mod.Desired, "0") - corrections = append(corrections, &models.Correction{ - Msg: mod.String(), - F: func() error { - _, err := api.client.DomainRecord.Create(context.Background(), dc.Name, &govultr.DomainRecordReq{Name: r.Name, Type: r.Type, Data: r.Data, TTL: r.TTL, Priority: &r.Priority}) - return err - }, - }) - } - return corrections, nil - } - changes, err := diff2.ByRecord(curRecords, dc, nil) if err != nil { From 134ce952a1bda78688f64df7940cc5ca289fb3c1 Mon Sep 17 00:00:00 2001 From: Tom Limoncelli Date: Sun, 1 Oct 2023 10:32:56 -0400 Subject: [PATCH 2/7] More diff1 removals --- .github/workflows/build.yml | 108 +--------- commands/previewPush.go | 4 +- commands/types/dnscontrol.d.ts | 114 +---------- documentation/byo-secrets.md | 4 +- documentation/functions/domain/IGNORE_NAME.md | 84 +------- .../functions/domain/IGNORE_TARGET.md | 38 ---- documentation/release-engineering.md | 4 +- integrationTest/integration_test.go | 19 -- pkg/js/helpers.js | 17 +- pkg/js/parse_tests/005-ignored-records.json | 25 ++- pkg/printer/printer.go | 7 +- providers/azuredns/azureDnsProvider.go | 190 +++++++++--------- providers/cloudflare/rest.go | 74 ------- providers/gandiv5/gandi_v5Provider.go | 13 -- providers/gcloud/gcloudProvider.go | 2 +- providers/route53/route53Provider.go | 7 - 16 files changed, 128 insertions(+), 582 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f7363f33bf..be477c0f71 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -90,111 +90,6 @@ jobs: ENV_CONTEXT: ${{ toJson(env) }} VARS_CONTEXT: ${{ toJson(vars) }} SECRETS_CONTEXT: ${{ toJson(secrets) }} - integrtests-diff1: - if: github.ref != 'refs/heads/master' && github.ref != 'refs/heads/main' - needs: integration-test-providers - runs-on: ubuntu-latest - container: - image: golang:1.20 - env: - TEST_RESULTS: "/tmp/test-results" - GOTESTSUM_FORMAT: testname - - # These providers will be tested if the env variable is set. - # Set it to the domain name to use during the test. - AZURE_DNS_DOMAIN: ${{ vars.AZURE_DNS_DOMAIN }} - BIND_DOMAIN: ${{ vars.BIND_DOMAIN }} - CLOUDFLAREAPI_DOMAIN: ${{ vars.CLOUDFLAREAPI_DOMAIN }} - CLOUDNS_DOMAIN: ${{ vars.CLOUDNS_DOMAIN }} - CSCGLOBAL_DOMAIN: ${{ vars.CSCGLOBAL_DOMAIN }} - DIGITALOCEAN_DOMAIN: ${{ vars.DIGITALOCEAN_DOMAIN }} - GANDI_V5_DOMAIN: ${{ vars.GANDI_V5_DOMAIN }} - GCLOUD_DOMAIN: ${{ vars.GCLOUD_DOMAIN }} - HEDNS_DOMAIN: ${{ vars.HEDNS_DOMAIN }} - HEXONET_DOMAIN: ${{ vars.HEXONET_DOMAIN }} - NAMEDOTCOM_DOMAIN: ${{ vars.NAMEDOTCOM_DOMAIN }} - NS1_DOMAIN: ${{ vars.NS1_DOMAIN }} - POWERDNS_DOMAIN: ${{ vars.POWERDNS_DOMAIN }} - ROUTE53_DOMAIN: ${{ vars.ROUTE53_DOMAIN }} - TRANSIP_DOMAIN: ${{ vars.TRANSIP_DOMAIN }} - - # The above providers have additional env variables they - # need for credentials and such. - - AZURE_DNS_CLIENT_ID: ${{ secrets.AZURE_DNS_CLIENT_ID }} - AZURE_DNS_CLIENT_SECRET: ${{ secrets.AZURE_DNS_CLIENT_SECRET }} - AZURE_DNS_RESOURCE_GROUP: ${{ secrets.AZURE_DNS_RESOURCE_GROUP }} - AZURE_DNS_SUBSCRIPTION_ID: ${{ secrets.AZURE_DNS_SUBSCRIPTION_ID }} - AZURE_DNS_TENANT_ID: ${{ secrets.AZURE_DNS_TENANT_ID }} - - CLOUDFLAREAPI_ACCOUNTID: ${{ secrets.CLOUDFLAREAPI_ACCOUNTID }} - CLOUDFLAREAPI_TOKEN: ${{ secrets.CLOUDFLAREAPI_TOKEN }} - - CLOUDNS_AUTH_ID: ${{ secrets.CLOUDNS_AUTH_ID }} - CLOUDNS_AUTH_PASSWORD: ${{ secrets.CLOUDNS_AUTH_PASSWORD }} - - CSCGLOBAL_APIKEY: ${{ secrets.CSCGLOBAL_APIKEY }} - CSCGLOBAL_USERTOKEN: ${{ secrets.CSCGLOBAL_USERTOKEN }} - - DIGITALOCEAN_TOKEN: ${{ secrets.DIGITALOCEAN_TOKEN }} - - GANDI_V5_APIKEY: ${{ secrets.GANDI_V5_APIKEY }} - - GCLOUD_EMAIL: ${{ secrets.GCLOUD_EMAIL }} - GCLOUD_PRIVATEKEY: ${{ secrets.GCLOUD_PRIVATEKEY }} - GCLOUD_PROJECT: ${{ secrets.GCLOUD_PROJECT }} - GCLOUD_TYPE: ${{ secrets.GCLOUD_TYPE }} - - HEDNS_PASSWORD: ${{ secrets.HEDNS_PASSWORD }} - HEDNS_TOTP_SECRET: ${{ secrets.HEDNS_TOTP_SECRET }} - HEDNS_USERNAME: ${{ secrets.HEDNS_USERNAME }} - - HEXONET_ENTITY: ${{ secrets.HEXONET_ENTITY }} - HEXONET_PW: ${{ secrets.HEXONET_PW }} - HEXONET_UID: ${{ secrets.HEXONET_UID }} - - NAMEDOTCOM_KEY: ${{ secrets.NAMEDOTCOM_KEY }} - NAMEDOTCOM_URL: ${{ secrets.NAMEDOTCOM_URL }} - NAMEDOTCOM_USER: ${{ secrets.NAMEDOTCOM_USER }} - - NS1_TOKEN: ${{ secrets.NS1_TOKEN }} - - POWERDNS_APIKEY: ${{ secrets.POWERDNS_APIKEY }} - POWERDNS_APIURL: ${{ secrets.POWERDNS_APIURL }} - POWERDNS_SERVERNAME: ${{ secrets.POWERDNS_SERVERNAME }} - - ROUTE53_KEY: ${{ secrets.ROUTE53_KEY }} - ROUTE53_KEY_ID: ${{ secrets.ROUTE53_KEY_ID }} - - TRANSIP_ACCOUNT_NAME: ${{ secrets.TRANSIP_ACCOUNT_NAME }} - TRANSIP_PRIVATE_KEY: ${{ secrets.TRANSIP_PRIVATE_KEY }} - - concurrency: ${{ matrix.provider }} - strategy: - fail-fast: false - matrix: - provider: ${{ fromJson(needs.integration-test-providers.outputs.integration_test_providers )}} - steps: - - uses: actions/checkout@v4 - - run: mkdir -p "$TEST_RESULTS" - - name: restore_cache - uses: actions/cache@v3.3.2 - with: - key: linux-go-${{ hashFiles('go.sum') }}-${{ env.cache-key }} - restore-keys: linux-go-${{ hashFiles('go.sum') }}-${{ env.cache-key }} - path: ${{ env.go-mod-path }} - - name: Run integration tests for ${{ matrix.provider }} provider - run: |- - if [ -z "$${{ matrix.provider }}_DOMAIN" ] ; then - echo "Skip test for ${{ matrix.provider }} provider" - else - go install gotest.tools/gotestsum@latest - gotestsum --junitfile ${TEST_RESULTS}/gotestsum-report.xml -- -timeout 30m -v -verbose -provider ${{ matrix.provider }} -cfworkers=false - fi - working-directory: integrationTest - - uses: actions/upload-artifact@v3.1.3 - with: - path: "/tmp/test-results" integrtests-diff2: if: github.ref != 'refs/heads/master' && github.ref != 'refs/heads/main' runs-on: ubuntu-latest @@ -202,7 +97,6 @@ jobs: image: golang:1.20 needs: - integration-test-providers - - integrtests-diff1 env: TEST_RESULTS: "/tmp/test-results" GOTESTSUM_FORMAT: testname @@ -294,7 +188,7 @@ jobs: run: |- go install gotest.tools/gotestsum@latest if [ -n "$${{ matrix.provider }}_DOMAIN" ] ; then - gotestsum --junitfile ${TEST_RESULTS}/gotestsum-report.xml -- -timeout 30m -v -verbose -provider ${{ matrix.provider }} -cfworkers=false -diff2 + gotestsum --junitfile ${TEST_RESULTS}/gotestsum-report.xml -- -timeout 30m -v -verbose -provider ${{ matrix.provider }} -cfworkers=false else echo "Skip test for ${{ matrix.provider }} provider" fi diff --git a/commands/previewPush.go b/commands/previewPush.go index fb5a400568..dfb1592ab9 100644 --- a/commands/previewPush.go +++ b/commands/previewPush.go @@ -236,15 +236,13 @@ func run(args PreviewArgs, push bool, interactive bool, out printer.CLI, report } reports, corrections, err := zonerecs.CorrectZoneRecords(provider.Driver, domain) - printReports(domain.Name, provider.Name, reports, out, push, notifier) out.EndProvider(provider.Name, len(corrections), err) if err != nil { anyErrors = true return } totalCorrections += len(corrections) - // When diff1 goes away, the call to printReports() should be moved to HERE. - //printReports(domain.Name, provider.Name, reports, out, push, notifier) + printReports(domain.Name, provider.Name, reports, out, push, notifier) reportItems = append(reportItems, ReportItem{ Domain: domain.Name, Corrections: len(corrections), diff --git a/commands/types/dnscontrol.d.ts b/commands/types/dnscontrol.d.ts index aa1aeada69..cc60f97377 100644 --- a/commands/types/dnscontrol.d.ts +++ b/commands/types/dnscontrol.d.ts @@ -1245,85 +1245,7 @@ declare function IGNORE(labelSpec: string, typeSpec?: string, targetSpec?: strin /** * `IGNORE_NAME(a)` is the same as `IGNORE(a, "*", "*")`. * - * ## Legacy mode ("diff1") - * - * When `--diff2=false` is used to revert to the old "diff1" algorithm, `IGNORE_NAME()` behaves as follows: - * - * WARNING: The `IGNORE_*` family of functions is risky to use. The code - * is brittle and has subtle bugs. Use at your own risk. Do not use these - * commands with `D_EXTEND()`. - * - * `IGNORE_NAME` can be used to ignore some records present in zone. - * Records of that name will be completely ignored. An optional `rTypes` may be specified as a comma separated list to only ignore records of the given type, e.g. `"A"`, `"A,CNAME"`, `"A, MX, CNAME"`. If `rTypes` is omitted or is `"*"` all record types matching the name will be ignored. - * - * `IGNORE_NAME` is like `NO_PURGE` except it acts only on some specific records instead of the whole zone. - * - * Technically `IGNORE_NAME` is a promise that DNSControl will not add, change, or delete records at a given label. This permits another entity to "own" that label. - * - * `IGNORE_NAME` is generally used in very specific situations: - * - * * Some records are managed by some other system and DNSControl is only used to manage some records and/or keep them updated. For example a DNS `A` record that is managed by a dynamic DNS client, or by Kubernetes External DNS, but DNSControl is used to manage the rest of the zone. In this case we don't want DNSControl to try to delete the externally managed record. - * * To work-around a pseudo record type that is not supported by DNSControl. For example some providers have a fake DNS record type called "URL" which creates a redirect. DNSControl normally deletes these records because it doesn't understand them. `IGNORE_NAME` will leave those records alone. - * - * In this example, DNSControl will insert/update the "baz.example.com" record but will leave unchanged the "foo.example.com" and "bar.example.com" ones. - * - * ```javascript - * D("example.com", REG_MY_PROVIDER, DnsProvider(DSP_MY_PROVIDER), - * IGNORE_NAME("foo"), // ignore all record types for name foo - * IGNORE_NAME("baz", "*"), // ignore all record types for name baz - * IGNORE_NAME("bar", "A,MX"), // ignore only A and MX records for name bar - * CNAME("bar", "www"), // CNAME is not ignored - * A("baz", "1.2.3.4") - * ); - * ``` - * - * `IGNORE_NAME` also supports glob patterns in the style of the [gobwas/glob](https://github.com/gobwas/glob) library. All of - * the following patterns will work: - * - * * `IGNORE_NAME("*.foo")` will ignore all records in the style of `bar.foo`, but will not ignore records using a double - * subdomain, such as `foo.bar.foo`. - * * `IGNORE_NAME("**.foo")` will ignore all subdomains of `foo`, including double subdomains. - * * `IGNORE_NAME("?oo")` will ignore all records of three symbols ending in `oo`, for example `foo` and `zoo`. It will - * not match `.` - * * `IGNORE_NAME("[abc]oo")` will ignore records `aoo`, `boo` and `coo`. `IGNORE_NAME("[a-c]oo")` is equivalent. - * * `IGNORE_NAME("[!abc]oo")` will ignore all three symbol records ending in `oo`, except for `aoo`, `boo`, `coo`. `IGNORE_NAME("[!a-c]oo")` is equivalent. - * * `IGNORE_NAME("{bar,[fz]oo}")` will ignore `bar`, `foo` and `zoo`. - * * `IGNORE_NAME("\\*.foo")` will ignore the literal record `*.foo`. - * - * # Caveats - * - * It is considered as an error to try to manage an ignored record. - * Ignoring a label is a promise that DNSControl won't meddle with - * anything at a particular label, therefore DNSControl prevents you from - * adding records at a label that is `IGNORE_NAME`'ed. - * - * Use `IGNORE_NAME("@")` to ignore at the domain's apex. Most providers - * insert magic or unchangeable records at the domain's apex; usually `NS` - * and `SOA` records. DNSControl treats them specially. - * - * # Errors - * - * * `trying to update/add IGNORE_NAME'd record: foo CNAME` - * - * This means you have both ignored `foo` and included a record (in this - * case, a CNAME) to update it. This is an error because `IGNORE_NAME` - * is a promise not to modify records at a certain label so that others - * may have free reign there. Therefore, DNSControl prevents you from - * modifying that label. - * - * The `foo CNAME` at the end of the message indicates the label name - * (`foo`) and the type of record (`CNAME`) that your dnsconfig.js file - * is trying to insert. - * - * You can override this error by adding the - * `IGNORE_NAME_DISABLE_SAFETY_CHECK` flag to the record. - * - * TXT("vpn", "this thing", IGNORE_NAME_DISABLE_SAFETY_CHECK) - * - * Disabling this safety check creates two risks: - * - * 1. Two owners (DNSControl and some other entity) toggling a record between two settings. - * 2. The other owner wiping all records at this label, which won't be noticed until the next time DNSControl is run. + * `IGNORE_NAME(a, b)` is the same as `IGNORE(a, b, "*")`. * * @see https://docs.dnscontrol.org/language-reference/domain-modifiers/ignore_name */ @@ -1334,40 +1256,6 @@ declare function IGNORE_NAME(pattern: string, rTypes?: string): DomainModifier; * * `IGNORE_TARGET_NAME(target, rtype)` is the same as `IGNORE("*", rtype, target)`. * - * ## Legacy mode ("diff1") - * - * When `--diff2=false` is used to revert to the old "diff1" algorithm, `IGNORE_NAME()` behaves as follows: - * - * WARNING: The `IGNORE_*` family of functions is risky to use. The code - * is brittle and has subtle bugs. Use at your own risk. Do not use these - * commands with `D_EXTEND()` or use it at the domain apex. - * - * IGNORE_TARGET can be used to ignore some records present in zone based on the record's target and type. IGNORE_TARGET currently only supports CNAME record types. - * - * IGNORE_TARGET is like NO_PURGE except it acts only on some specific records instead of the whole zone. - * - * IGNORE_TARGET is generally used in very specific situations: - * - * * Some records are managed by some other system and DNSControl is only used to manage some records and/or keep them updated. For example a DNS record that is created by AWS Certificate Manager for validation, but DNSControl is used to manage the rest of the zone. In this case we don't want DNSControl to try to delete the externally managed record. - * - * In this example, DNSControl will insert/update the "baz.example.com" record but will leave unchanged a CNAME to "foo.acm-validations.aws" record. - * - * ```javascript - * D("example.com", REG_MY_PROVIDER, DnsProvider(DSP_MY_PROVIDER), - * IGNORE_TARGET("**.acm-validations.aws.", "CNAME"), - * A("baz", "1.2.3.4") - * ); - * ``` - * - * IGNORE_TARGET also supports glob patterns in the style of the [gobwas/glob](https://github.com/gobwas/glob#example) library. Some example patterns: - * - * * `IGNORE_TARGET("example.com", "CNAME")` will ignore all CNAME records with targets of exactly `example.com`. - * * `IGNORE_TARGET("*.foo", "CNAME")` will ignore all CNAME records with targets in the style of `bar.foo`, but will not ignore records with targets using a double subdomain, such as `foo.bar.foo`. - * * `IGNORE_TARGET("**.bar", "CNAME")` will ignore all CNAME records with target subdomains of `bar`, including double subdomains such as `www.foo.bar`. - * * `IGNORE_TARGET("dev.*.foo", "CNAME")` will ignore all CNAME records with targets in the style of `dev.bar.foo`, but will not ignore records with targets using a double subdomain, such as `dev.foo.bar.foo`. - * - * It is considered as an error to try to manage an ignored record. - * * @see https://docs.dnscontrol.org/language-reference/domain-modifiers/ignore_target */ declare function IGNORE_TARGET(pattern: string, rType: string): DomainModifier; diff --git a/documentation/byo-secrets.md b/documentation/byo-secrets.md index 93ec0eafef..7b2e41ed57 100644 --- a/documentation/byo-secrets.md +++ b/documentation/byo-secrets.md @@ -50,7 +50,7 @@ only run if they have access to the secrets they will need. # How it works Tests are executed if `*_DOMAIN` exists where `*` is the name of the provider. If the value is empty or -unset, the test is skipped. +unset, the test is skipped. For example, if a provider is called `FANCYDNS`, there must be a secret called `FANCYDNS_DOMAIN`. @@ -79,7 +79,7 @@ The line looks something like: 2. Add your providers `_DOMAIN` env variable: -Add it to the `env` section of `integrtests-diff1` and again in `integrtests-diff2`. +Add it to the `env` section of `integrtests-diff2`. For example, the entry for BIND looks like: diff --git a/documentation/functions/domain/IGNORE_NAME.md b/documentation/functions/domain/IGNORE_NAME.md index 507c274f8a..e249baf15f 100644 --- a/documentation/functions/domain/IGNORE_NAME.md +++ b/documentation/functions/domain/IGNORE_NAME.md @@ -10,86 +10,4 @@ parameter_types: `IGNORE_NAME(a)` is the same as `IGNORE(a, "*", "*")`. -## Legacy mode ("diff1") - -When `--diff2=false` is used to revert to the old "diff1" algorithm, `IGNORE_NAME()` behaves as follows: - -{% hint style="warning" %} -**WARNING**: The `IGNORE_*` family of functions is risky to use. The code -is brittle and has subtle bugs. Use at your own risk. Do not use these -commands with `D_EXTEND()`. -{% endhint %} - -`IGNORE_NAME` can be used to ignore some records present in zone. -Records of that name will be completely ignored. An optional `rTypes` may be specified as a comma separated list to only ignore records of the given type, e.g. `"A"`, `"A,CNAME"`, `"A, MX, CNAME"`. If `rTypes` is omitted or is `"*"` all record types matching the name will be ignored. - -`IGNORE_NAME` is like `NO_PURGE` except it acts only on some specific records instead of the whole zone. - -Technically `IGNORE_NAME` is a promise that DNSControl will not add, change, or delete records at a given label. This permits another entity to "own" that label. - -`IGNORE_NAME` is generally used in very specific situations: - -* Some records are managed by some other system and DNSControl is only used to manage some records and/or keep them updated. For example a DNS `A` record that is managed by a dynamic DNS client, or by Kubernetes External DNS, but DNSControl is used to manage the rest of the zone. In this case we don't want DNSControl to try to delete the externally managed record. -* To work-around a pseudo record type that is not supported by DNSControl. For example some providers have a fake DNS record type called "URL" which creates a redirect. DNSControl normally deletes these records because it doesn't understand them. `IGNORE_NAME` will leave those records alone. - -In this example, DNSControl will insert/update the "baz.example.com" record but will leave unchanged the "foo.example.com" and "bar.example.com" ones. - -{% code title="dnsconfig.js" %} -```javascript -D("example.com", REG_MY_PROVIDER, DnsProvider(DSP_MY_PROVIDER), - IGNORE_NAME("foo"), // ignore all record types for name foo - IGNORE_NAME("baz", "*"), // ignore all record types for name baz - IGNORE_NAME("bar", "A,MX"), // ignore only A and MX records for name bar - CNAME("bar", "www"), // CNAME is not ignored - A("baz", "1.2.3.4") -); -``` -{% endcode %} - -`IGNORE_NAME` also supports glob patterns in the style of the [gobwas/glob](https://github.com/gobwas/glob) library. All of -the following patterns will work: - -* `IGNORE_NAME("*.foo")` will ignore all records in the style of `bar.foo`, but will not ignore records using a double -subdomain, such as `foo.bar.foo`. -* `IGNORE_NAME("**.foo")` will ignore all subdomains of `foo`, including double subdomains. -* `IGNORE_NAME("?oo")` will ignore all records of three symbols ending in `oo`, for example `foo` and `zoo`. It will -not match `.` -* `IGNORE_NAME("[abc]oo")` will ignore records `aoo`, `boo` and `coo`. `IGNORE_NAME("[a-c]oo")` is equivalent. -* `IGNORE_NAME("[!abc]oo")` will ignore all three symbol records ending in `oo`, except for `aoo`, `boo`, `coo`. `IGNORE_NAME("[!a-c]oo")` is equivalent. -* `IGNORE_NAME("{bar,[fz]oo}")` will ignore `bar`, `foo` and `zoo`. -* `IGNORE_NAME("\\*.foo")` will ignore the literal record `*.foo`. - -# Caveats - -It is considered as an error to try to manage an ignored record. -Ignoring a label is a promise that DNSControl won't meddle with -anything at a particular label, therefore DNSControl prevents you from -adding records at a label that is `IGNORE_NAME`'ed. - -Use `IGNORE_NAME("@")` to ignore at the domain's apex. Most providers -insert magic or unchangeable records at the domain's apex; usually `NS` -and `SOA` records. DNSControl treats them specially. - -# Errors - -* `trying to update/add IGNORE_NAME'd record: foo CNAME` - -This means you have both ignored `foo` and included a record (in this -case, a CNAME) to update it. This is an error because `IGNORE_NAME` -is a promise not to modify records at a certain label so that others -may have free reign there. Therefore, DNSControl prevents you from -modifying that label. - -The `foo CNAME` at the end of the message indicates the label name -(`foo`) and the type of record (`CNAME`) that your dnsconfig.js file -is trying to insert. - -You can override this error by adding the -`IGNORE_NAME_DISABLE_SAFETY_CHECK` flag to the record. - - TXT("vpn", "this thing", IGNORE_NAME_DISABLE_SAFETY_CHECK) - -Disabling this safety check creates two risks: - -1. Two owners (DNSControl and some other entity) toggling a record between two settings. -2. The other owner wiping all records at this label, which won't be noticed until the next time DNSControl is run. +`IGNORE_NAME(a, b)` is the same as `IGNORE(a, b, "*")`. diff --git a/documentation/functions/domain/IGNORE_TARGET.md b/documentation/functions/domain/IGNORE_TARGET.md index a7c772cd14..94d660ffdb 100644 --- a/documentation/functions/domain/IGNORE_TARGET.md +++ b/documentation/functions/domain/IGNORE_TARGET.md @@ -11,41 +11,3 @@ parameter_types: `IGNORE_TARGET_NAME(target)` is the same as `IGNORE("*", "*", target)`. `IGNORE_TARGET_NAME(target, rtype)` is the same as `IGNORE("*", rtype, target)`. - -## Legacy mode ("diff1") - -When `--diff2=false` is used to revert to the old "diff1" algorithm, `IGNORE_NAME()` behaves as follows: - -{% hint style="warning" %} -**WARNING**: The `IGNORE_*` family of functions is risky to use. The code -is brittle and has subtle bugs. Use at your own risk. Do not use these -commands with `D_EXTEND()` or use it at the domain apex. -{% endhint %} - -IGNORE_TARGET can be used to ignore some records present in zone based on the record's target and type. IGNORE_TARGET currently only supports CNAME record types. - -IGNORE_TARGET is like NO_PURGE except it acts only on some specific records instead of the whole zone. - -IGNORE_TARGET is generally used in very specific situations: - -* Some records are managed by some other system and DNSControl is only used to manage some records and/or keep them updated. For example a DNS record that is created by AWS Certificate Manager for validation, but DNSControl is used to manage the rest of the zone. In this case we don't want DNSControl to try to delete the externally managed record. - -In this example, DNSControl will insert/update the "baz.example.com" record but will leave unchanged a CNAME to "foo.acm-validations.aws" record. - -{% code title="dnsconfig.js" %} -```javascript -D("example.com", REG_MY_PROVIDER, DnsProvider(DSP_MY_PROVIDER), - IGNORE_TARGET("**.acm-validations.aws.", "CNAME"), - A("baz", "1.2.3.4") -); -``` -{% endcode %} - -IGNORE_TARGET also supports glob patterns in the style of the [gobwas/glob](https://github.com/gobwas/glob#example) library. Some example patterns: - -* `IGNORE_TARGET("example.com", "CNAME")` will ignore all CNAME records with targets of exactly `example.com`. -* `IGNORE_TARGET("*.foo", "CNAME")` will ignore all CNAME records with targets in the style of `bar.foo`, but will not ignore records with targets using a double subdomain, such as `foo.bar.foo`. -* `IGNORE_TARGET("**.bar", "CNAME")` will ignore all CNAME records with target subdomains of `bar`, including double subdomains such as `www.foo.bar`. -* `IGNORE_TARGET("dev.*.foo", "CNAME")` will ignore all CNAME records with targets in the style of `dev.bar.foo`, but will not ignore records with targets using a double subdomain, such as `dev.foo.bar.foo`. - -It is considered as an error to try to manage an ignored record. diff --git a/documentation/release-engineering.md b/documentation/release-engineering.md index bdb27d6b8d..17e21f07d6 100644 --- a/documentation/release-engineering.md +++ b/documentation/release-engineering.md @@ -130,7 +130,7 @@ find * -name \*.bak -delete GHA is configured to run an integration test for any provider listed in the "provider" list. However the test is skipped if the `*_DOMAIN` variable is not set. For example, the Google Cloud provider integration test is only run if `GCLOUD_DOMAIN` is set. * Q: Where is the list of providers to run integration tests on? -* A: In `.github/workflows/build.yml`: (1) the "PROVIDERS" list, (2) the `integrtests-diff1` section, (3) the `integrtests-diff2` section. +* A: In `.github/workflows/build.yml`: (1) the "PROVIDERS" list, (2) the `integrtests-diff2` section. * Q: Where are non-secret environment variables stored? * A: GHA calls them "Variables". Update them here: https://github.com/StackExchange/dnscontrol/settings/variables/actions @@ -143,7 +143,7 @@ GHA is configured to run an integration test for any provider listed in the "pro 1. Edit `.github/workflows/build.yml` 2. Add the `FOO_DOMAIN` variable name of the provider to the "PROVIDERS" list. 3. Set the `FOO_DOMAIN` variables in GHA via https://github.com/StackExchange/dnscontrol/settings/variables/actions -4. All other variables should be stored as secrets (for consistency). Add them to both the `integrtests-diff1` section and the `integrtests-diff2` section. +4. All other variables should be stored as secrets (for consistency). Add them to the `integrtests-diff2` section. Set them in GHA via https://github.com/StackExchange/dnscontrol/settings/secrets/actions ### How do I add a "bring your own keys" integration test? diff --git a/integrationTest/integration_test.go b/integrationTest/integration_test.go index 4913bfb1f9..ff8c216aeb 100644 --- a/integrationTest/integration_test.go +++ b/integrationTest/integration_test.go @@ -531,26 +531,10 @@ func ds(name string, keyTag uint16, algorithm, digestType uint8, digest string) func ignoreName(labelSpec string) *models.RecordConfig { return ignore(labelSpec, "*", "*") - // r := &models.RecordConfig{ - // Type: "IGNORE_NAME", - // Metadata: map[string]string{}, - // } - // SetLabel(r, labelSpec, "**current-domain**") - - // r.Metadata["ignore_LabelPattern"] = labelSpec - // return r } func ignoreTarget(targetSpec string, typeSpec string) *models.RecordConfig { return ignore("*", "*", targetSpec) - // r := &models.RecordConfig{ - // Type: "IGNORE_TARGET", - // Metadata: map[string]string{}, - // } - - // r.Metadata["ignore_RTypePattern"] = typeSpec - // r.Metadata["ignore_TargetPattern"] = typeSpec - // return r } func ignore(labelSpec string, typeSpec string, targetSpec string) *models.RecordConfig { @@ -2001,9 +1985,6 @@ func makeTests(t *testing.T) []*TestGroup { ), // https://github.com/StackExchange/dnscontrol/issues/2285 - // IGNORE_TARGET for CNAMEs wasn't working for AZURE_DNS. - // Interestingly enough, this has never worked with - // GANDI_V5/diff1. It works on all providers in diff2. testgroup("IGNORE_TARGET b2285", tc("Create some records", cname("foo", "redact1.acm-validations.aws."), diff --git a/pkg/js/helpers.js b/pkg/js/helpers.js index 68bb342f34..df15af2ca2 100644 --- a/pkg/js/helpers.js +++ b/pkg/js/helpers.js @@ -853,24 +853,11 @@ function IGNORE(labelPattern, rtypePattern, targetPattern) { // IGNORE_NAME(name, rTypes) function IGNORE_NAME(name, rTypes) { - if (rTypes === undefined) { - rTypes = '*'; - } - return function (d) { - d.unmanaged.push({ - label_pattern: name, - rType_pattern: rTypes, - }); - }; + return IGNORE(name, rTypes) } function IGNORE_TARGET(target, rType) { - return function (d) { - d.unmanaged.push({ - rType_pattern: rType, - target_pattern: target, - }); - }; + return IGNORE("*", rType, target) } // IMPORT_TRANSFORM(translation_table, domain) diff --git a/pkg/js/parse_tests/005-ignored-records.json b/pkg/js/parse_tests/005-ignored-records.json index 2ea0afa028..faa455afa6 100644 --- a/pkg/js/parse_tests/005-ignored-records.json +++ b/pkg/js/parse_tests/005-ignored-records.json @@ -10,21 +10,26 @@ "unmanaged": [ { "label_pattern": "testignore", - "rType_pattern": "*" + "rType_pattern": "*", + "target_pattern": "*" }, { "label_pattern": "testignore2", - "rType_pattern": "A" + "rType_pattern": "A", + "target_pattern": "*" }, { "label_pattern": "testignore3", - "rType_pattern": "A, CNAME, TXT" + "rType_pattern": "A, CNAME, TXT", + "target_pattern": "*" }, { "label_pattern": "testignore4", - "rType_pattern": "*" + "rType_pattern": "*", + "target_pattern": "*" }, { + "label_pattern": "*", "rType_pattern": "CNAME", "target_pattern": "testtarget" }, @@ -35,9 +40,11 @@ }, { "label_pattern": "@", - "rType_pattern": "*" + "rType_pattern": "*", + "target_pattern": "*" }, { + "label_pattern": "*", "rType_pattern": "CNAME", "target_pattern": "@" } @@ -79,16 +86,20 @@ }, { "label_pattern": "nametest", - "rType_pattern": "*" + "rType_pattern": "*", + "target_pattern": "*" }, { + "label_pattern": "*", + "rType_pattern": "*", "target_pattern": "targettest1" }, { + "label_pattern": "*", "rType_pattern": "A", "target_pattern": "targettest2" } ] } ] -} +} \ No newline at end of file diff --git a/pkg/printer/printer.go b/pkg/printer/printer.go index f2ef7ce575..1a7aa3251e 100644 --- a/pkg/printer/printer.go +++ b/pkg/printer/printer.go @@ -92,9 +92,10 @@ func (c ConsolePrinter) PrintCorrection(i int, correction *models.Correction) { // PrintReport is called to print/format each non-mutating correction (diff2.REPORT). func (c ConsolePrinter) PrintReport(i int, correction *models.Correction) { - // When diff1 is eliminated: - //fmt.Fprintf(c.Writer, "INFO#%d: %s\n", i+1, correction.Msg) - fmt.Fprintf(c.Writer, "INFO: %s\n", correction.Msg) + // when diff1 is eliminated: + fmt.Fprintf(c.Writer, "INFO#%d: %s\n", i+1, correction.Msg) + // When diff1 exists: + //fmt.Fprintf(c.Writer, "INFO: %s\n", correction.Msg) } // PromptToRun prompts the user to see if they want to execute a correction. diff --git a/providers/azuredns/azureDnsProvider.go b/providers/azuredns/azureDnsProvider.go index dd6c3f498a..8c400f1570 100644 --- a/providers/azuredns/azureDnsProvider.go +++ b/providers/azuredns/azureDnsProvider.go @@ -276,34 +276,34 @@ func (a *azurednsProvider) recordDelete(zoneName string, reckey models.RecordKey return err } -func nativeToRecordType(recordType *string) (adns.RecordType, error) { - recordTypeStripped := strings.TrimPrefix(*recordType, "Microsoft.Network/dnszones/") - switch recordTypeStripped { - case "A", "AZURE_ALIAS_A": - return adns.RecordTypeA, nil - case "AAAA", "AZURE_ALIAS_AAAA": - return adns.RecordTypeAAAA, nil - case "CAA": - return adns.RecordTypeCAA, nil - case "CNAME", "AZURE_ALIAS_CNAME": - return adns.RecordTypeCNAME, nil - case "MX": - return adns.RecordTypeMX, nil - case "NS": - return adns.RecordTypeNS, nil - case "PTR": - return adns.RecordTypePTR, nil - case "SRV": - return adns.RecordTypeSRV, nil - case "TXT": - return adns.RecordTypeTXT, nil - case "SOA": - return adns.RecordTypeSOA, nil - default: - // Unimplemented type. Return adns.A as a decoy, but send an error. - return adns.RecordTypeA, fmt.Errorf("nativeToRecordType rtype %v unimplemented", *recordType) - } -} +// func nativeToRecordType(recordType *string) (adns.RecordType, error) { +// recordTypeStripped := strings.TrimPrefix(*recordType, "Microsoft.Network/dnszones/") +// switch recordTypeStripped { +// case "A", "AZURE_ALIAS_A": +// return adns.RecordTypeA, nil +// case "AAAA", "AZURE_ALIAS_AAAA": +// return adns.RecordTypeAAAA, nil +// case "CAA": +// return adns.RecordTypeCAA, nil +// case "CNAME", "AZURE_ALIAS_CNAME": +// return adns.RecordTypeCNAME, nil +// case "MX": +// return adns.RecordTypeMX, nil +// case "NS": +// return adns.RecordTypeNS, nil +// case "PTR": +// return adns.RecordTypePTR, nil +// case "SRV": +// return adns.RecordTypeSRV, nil +// case "TXT": +// return adns.RecordTypeTXT, nil +// case "SOA": +// return adns.RecordTypeSOA, nil +// default: +// // Unimplemented type. Return adns.A as a decoy, but send an error. +// return adns.RecordTypeA, fmt.Errorf("nativeToRecordType rtype %v unimplemented", *recordType) +// } +// } func nativeToRecordTypeDiff2(recordType *string) (adns.RecordType, error) { recordTypeStripped := strings.TrimPrefix(*recordType, "Microsoft.Network/dnszones/") @@ -483,73 +483,73 @@ func nativeToRecords(set *adns.RecordSet, origin string) []*models.RecordConfig return results } -func (a *azurednsProvider) recordToNative(recordKey models.RecordKey, recordConfig []*models.RecordConfig) (*adns.RecordSet, adns.RecordType, error) { - recordSet := &adns.RecordSet{Type: to.StringPtr(recordKey.Type), Properties: &adns.RecordSetProperties{}} - for _, rec := range recordConfig { - switch recordKey.Type { - case "A": - if recordSet.Properties.ARecords == nil { - recordSet.Properties.ARecords = []*adns.ARecord{} - } - recordSet.Properties.ARecords = append(recordSet.Properties.ARecords, &adns.ARecord{IPv4Address: to.StringPtr(rec.GetTargetField())}) - case "AAAA": - if recordSet.Properties.AaaaRecords == nil { - recordSet.Properties.AaaaRecords = []*adns.AaaaRecord{} - } - recordSet.Properties.AaaaRecords = append(recordSet.Properties.AaaaRecords, &adns.AaaaRecord{IPv6Address: to.StringPtr(rec.GetTargetField())}) - case "CNAME": - recordSet.Properties.CnameRecord = &adns.CnameRecord{Cname: to.StringPtr(rec.GetTargetField())} - case "NS": - if recordSet.Properties.NsRecords == nil { - recordSet.Properties.NsRecords = []*adns.NsRecord{} - } - recordSet.Properties.NsRecords = append(recordSet.Properties.NsRecords, &adns.NsRecord{Nsdname: to.StringPtr(rec.GetTargetField())}) - case "PTR": - if recordSet.Properties.PtrRecords == nil { - recordSet.Properties.PtrRecords = []*adns.PtrRecord{} - } - recordSet.Properties.PtrRecords = append(recordSet.Properties.PtrRecords, &adns.PtrRecord{Ptrdname: to.StringPtr(rec.GetTargetField())}) - case "TXT": - if recordSet.Properties.TxtRecords == nil { - recordSet.Properties.TxtRecords = []*adns.TxtRecord{} - } - // Empty TXT record needs to have no value set in it's properties - if !(len(rec.TxtStrings) == 1 && rec.TxtStrings[0] == "") { - var txts []*string - for _, txt := range rec.TxtStrings { - txts = append(txts, to.StringPtr(txt)) - } - recordSet.Properties.TxtRecords = append(recordSet.Properties.TxtRecords, &adns.TxtRecord{Value: txts}) - } - case "MX": - if recordSet.Properties.MxRecords == nil { - recordSet.Properties.MxRecords = []*adns.MxRecord{} - } - recordSet.Properties.MxRecords = append(recordSet.Properties.MxRecords, &adns.MxRecord{Exchange: to.StringPtr(rec.GetTargetField()), Preference: to.Int32Ptr(int32(rec.MxPreference))}) - case "SRV": - if recordSet.Properties.SrvRecords == nil { - recordSet.Properties.SrvRecords = []*adns.SrvRecord{} - } - recordSet.Properties.SrvRecords = append(recordSet.Properties.SrvRecords, &adns.SrvRecord{Target: to.StringPtr(rec.GetTargetField()), Port: to.Int32Ptr(int32(rec.SrvPort)), Weight: to.Int32Ptr(int32(rec.SrvWeight)), Priority: to.Int32Ptr(int32(rec.SrvPriority))}) - case "CAA": - if recordSet.Properties.CaaRecords == nil { - recordSet.Properties.CaaRecords = []*adns.CaaRecord{} - } - recordSet.Properties.CaaRecords = append(recordSet.Properties.CaaRecords, &adns.CaaRecord{Value: to.StringPtr(rec.GetTargetField()), Tag: to.StringPtr(rec.CaaTag), Flags: to.Int32Ptr(int32(rec.CaaFlag))}) - case "AZURE_ALIAS_A", "AZURE_ALIAS_AAAA", "AZURE_ALIAS_CNAME": - *recordSet.Type = rec.AzureAlias["type"] - recordSet.Properties.TargetResource = &adns.SubResource{ID: to.StringPtr(rec.GetTargetField())} - default: - return nil, adns.RecordTypeA, fmt.Errorf("recordToNative rtype %v unimplemented", recordKey.Type) // ands.A is a placeholder - } - } - - rt, err := nativeToRecordType(to.StringPtr(*recordSet.Type)) - if err != nil { - return nil, adns.RecordTypeA, err // adns.A is a placeholder - } - return recordSet, rt, nil -} +// func (a *azurednsProvider) recordToNative(recordKey models.RecordKey, recordConfig []*models.RecordConfig) (*adns.RecordSet, adns.RecordType, error) { +// recordSet := &adns.RecordSet{Type: to.StringPtr(recordKey.Type), Properties: &adns.RecordSetProperties{}} +// for _, rec := range recordConfig { +// switch recordKey.Type { +// case "A": +// if recordSet.Properties.ARecords == nil { +// recordSet.Properties.ARecords = []*adns.ARecord{} +// } +// recordSet.Properties.ARecords = append(recordSet.Properties.ARecords, &adns.ARecord{IPv4Address: to.StringPtr(rec.GetTargetField())}) +// case "AAAA": +// if recordSet.Properties.AaaaRecords == nil { +// recordSet.Properties.AaaaRecords = []*adns.AaaaRecord{} +// } +// recordSet.Properties.AaaaRecords = append(recordSet.Properties.AaaaRecords, &adns.AaaaRecord{IPv6Address: to.StringPtr(rec.GetTargetField())}) +// case "CNAME": +// recordSet.Properties.CnameRecord = &adns.CnameRecord{Cname: to.StringPtr(rec.GetTargetField())} +// case "NS": +// if recordSet.Properties.NsRecords == nil { +// recordSet.Properties.NsRecords = []*adns.NsRecord{} +// } +// recordSet.Properties.NsRecords = append(recordSet.Properties.NsRecords, &adns.NsRecord{Nsdname: to.StringPtr(rec.GetTargetField())}) +// case "PTR": +// if recordSet.Properties.PtrRecords == nil { +// recordSet.Properties.PtrRecords = []*adns.PtrRecord{} +// } +// recordSet.Properties.PtrRecords = append(recordSet.Properties.PtrRecords, &adns.PtrRecord{Ptrdname: to.StringPtr(rec.GetTargetField())}) +// case "TXT": +// if recordSet.Properties.TxtRecords == nil { +// recordSet.Properties.TxtRecords = []*adns.TxtRecord{} +// } +// // Empty TXT record needs to have no value set in it's properties +// if !(len(rec.TxtStrings) == 1 && rec.TxtStrings[0] == "") { +// var txts []*string +// for _, txt := range rec.TxtStrings { +// txts = append(txts, to.StringPtr(txt)) +// } +// recordSet.Properties.TxtRecords = append(recordSet.Properties.TxtRecords, &adns.TxtRecord{Value: txts}) +// } +// case "MX": +// if recordSet.Properties.MxRecords == nil { +// recordSet.Properties.MxRecords = []*adns.MxRecord{} +// } +// recordSet.Properties.MxRecords = append(recordSet.Properties.MxRecords, &adns.MxRecord{Exchange: to.StringPtr(rec.GetTargetField()), Preference: to.Int32Ptr(int32(rec.MxPreference))}) +// case "SRV": +// if recordSet.Properties.SrvRecords == nil { +// recordSet.Properties.SrvRecords = []*adns.SrvRecord{} +// } +// recordSet.Properties.SrvRecords = append(recordSet.Properties.SrvRecords, &adns.SrvRecord{Target: to.StringPtr(rec.GetTargetField()), Port: to.Int32Ptr(int32(rec.SrvPort)), Weight: to.Int32Ptr(int32(rec.SrvWeight)), Priority: to.Int32Ptr(int32(rec.SrvPriority))}) +// case "CAA": +// if recordSet.Properties.CaaRecords == nil { +// recordSet.Properties.CaaRecords = []*adns.CaaRecord{} +// } +// recordSet.Properties.CaaRecords = append(recordSet.Properties.CaaRecords, &adns.CaaRecord{Value: to.StringPtr(rec.GetTargetField()), Tag: to.StringPtr(rec.CaaTag), Flags: to.Int32Ptr(int32(rec.CaaFlag))}) +// case "AZURE_ALIAS_A", "AZURE_ALIAS_AAAA", "AZURE_ALIAS_CNAME": +// *recordSet.Type = rec.AzureAlias["type"] +// recordSet.Properties.TargetResource = &adns.SubResource{ID: to.StringPtr(rec.GetTargetField())} +// default: +// return nil, adns.RecordTypeA, fmt.Errorf("recordToNative rtype %v unimplemented", recordKey.Type) // ands.A is a placeholder +// } +// } + +// rt, err := nativeToRecordType(to.StringPtr(*recordSet.Type)) +// if err != nil { +// return nil, adns.RecordTypeA, err // adns.A is a placeholder +// } +// return recordSet, rt, nil +// } // NOTE recordToNativeDiff2 is really "convert []RecordConfig to rrset". diff --git a/providers/cloudflare/rest.go b/providers/cloudflare/rest.go index 0f6bebfec0..8b8c708459 100644 --- a/providers/cloudflare/rest.go +++ b/providers/cloudflare/rest.go @@ -54,17 +54,6 @@ func (c *cloudflareProvider) deleteDNSRecord(rec cloudflare.DNSRecord, domainID return c.cfClient.DeleteDNSRecord(context.Background(), cloudflare.ZoneIdentifier(domainID), rec.ID) } -// create a correction to delete a record -func (c *cloudflareProvider) deleteRec(rec cloudflare.DNSRecord, domainID string) *models.Correction { - return &models.Correction{ - Msg: fmt.Sprintf("DELETE record: %s %s %d %q (id=%s)", rec.Name, rec.Type, rec.TTL, rec.Content, rec.ID), - F: func() error { - err := c.cfClient.DeleteDNSRecord(context.Background(), cloudflare.ZoneIdentifier(domainID), rec.ID) - return err - }, - } -} - func (c *cloudflareProvider) createZone(domainName string) (string, error) { zone, err := c.cfClient.CreateZone(context.Background(), domainName, false, cloudflare.Account{ID: c.accountID}, "full") return zone.ID, err @@ -129,69 +118,6 @@ func cfNaptrData(rec *models.RecordConfig) *cfNaptrRecData { } } -func (c *cloudflareProvider) createRec(rec *models.RecordConfig, domainID string) []*models.Correction { - var id string - content := rec.GetTargetField() - if rec.Metadata[metaOriginalIP] != "" { - content = rec.Metadata[metaOriginalIP] - } - prio := "" - if rec.Type == "MX" { - prio = fmt.Sprintf(" %d ", rec.MxPreference) - } - if rec.Type == "TXT" { - content = rec.GetTargetTXTJoined() - } - if rec.Type == "DS" { - content = fmt.Sprintf("%d %d %d %s", rec.DsKeyTag, rec.DsAlgorithm, rec.DsDigestType, rec.DsDigest) - } - arr := []*models.Correction{{ - Msg: fmt.Sprintf("CREATE record: %s %s %d%s %s", rec.GetLabel(), rec.Type, rec.TTL, prio, content), - F: func() error { - cf := cloudflare.CreateDNSRecordParams{ - Name: rec.GetLabel(), - Type: rec.Type, - TTL: int(rec.TTL), - Content: content, - Priority: &rec.MxPreference, - } - if rec.Type == "SRV" { - cf.Data = cfSrvData(rec) - cf.Name = rec.GetLabelFQDN() - } else if rec.Type == "CAA" { - cf.Data = cfCaaData(rec) - cf.Name = rec.GetLabelFQDN() - cf.Content = "" - } else if rec.Type == "TLSA" { - cf.Data = cfTlsaData(rec) - cf.Name = rec.GetLabelFQDN() - } else if rec.Type == "SSHFP" { - cf.Data = cfSshfpData(rec) - cf.Name = rec.GetLabelFQDN() - } else if rec.Type == "DS" { - cf.Data = cfDSData(rec) - } else if rec.Type == "NAPTR" { - cf.Data = cfNaptrData(rec) - cf.Name = rec.GetLabelFQDN() - } - resp, err := c.cfClient.CreateDNSRecord(context.Background(), cloudflare.ZoneIdentifier(domainID), cf) - if err != nil { - return err - } - // Updating id (from the outer scope) by side-effect, required for updating proxy mode - id = resp.ID - return nil - }, - }} - if rec.Metadata[metaProxy] != "off" { - arr = append(arr, &models.Correction{ - Msg: fmt.Sprintf("ACTIVATE PROXY for new record %s %s %d %s", rec.GetLabel(), rec.Type, rec.TTL, rec.GetTargetField()), - F: func() error { return c.modifyRecord(domainID, id, true, rec) }, - }) - } - return arr -} - func (c *cloudflareProvider) createRecDiff2(rec *models.RecordConfig, domainID string, msg string) []*models.Correction { content := rec.GetTargetField() diff --git a/providers/gandiv5/gandi_v5Provider.go b/providers/gandiv5/gandi_v5Provider.go index 02838bcfd0..fd9f2bc3fb 100644 --- a/providers/gandiv5/gandi_v5Provider.go +++ b/providers/gandiv5/gandi_v5Provider.go @@ -296,19 +296,6 @@ func debugRecords(note string, recs []*models.RecordConfig) { } } -// gatherAffectedLabels takes the output of diff.ChangedGroups and -// regroups it by FQDN of the label, not by Key. It also returns -// a list of all the FQDNs. -func gatherAffectedLabels(groups map[models.RecordKey][]string) (labels map[string]bool, msgs map[string][]string) { - labels = map[string]bool{} - msgs = map[string][]string{} - for k, v := range groups { - labels[k.NameFQDN] = true - msgs[k.NameFQDN] = append(msgs[k.NameFQDN], v...) - } - return labels, msgs -} - // Section 3: Registrar-related functions // GetNameservers returns a list of nameservers for domain. diff --git a/providers/gcloud/gcloudProvider.go b/providers/gcloud/gcloudProvider.go index b81e0a9281..c20cf4a84e 100644 --- a/providers/gcloud/gcloudProvider.go +++ b/providers/gcloud/gcloudProvider.go @@ -60,7 +60,7 @@ type gcloudProvider struct { project string nameServerSet *string zones map[string]*gdns.ManagedZone - // diff1 + // For use with diff / NewComnpat() oldRRsMap map[string]map[key]*gdns.ResourceRecordSet zoneNameMap map[string]string // provider metadata fields diff --git a/providers/route53/route53Provider.go b/providers/route53/route53Provider.go index cf540f01fc..fd3e7dd1f9 100644 --- a/providers/route53/route53Provider.go +++ b/providers/route53/route53Provider.go @@ -507,13 +507,6 @@ func nativeToRecords(set r53Types.ResourceRecordSet, origin string) ([]*models.R return results, nil } -func getAliasMap(r *models.RecordConfig) map[string]string { - if r.Type != "R53_ALIAS" { - return nil - } - return r.R53Alias -} - func aliasToRRSet(zone r53Types.HostedZone, r *models.RecordConfig) *r53Types.ResourceRecordSet { target := r.GetTargetField() zoneID := getZoneID(zone, r) From 3b5af12023d63fddb058e9737ce9177aed605162 Mon Sep 17 00:00:00 2001 From: Tom Limoncelli Date: Sun, 1 Oct 2023 10:46:25 -0400 Subject: [PATCH 3/7] another batch of updates --- pkg/diff/diff.go | 345 +-------------------- pkg/diff/diff2compat.go | 3 - pkg/diff/diff_test.go | 365 ----------------------- providers/hostingde/hostingdeProvider.go | 2 +- providers/netcup/netcupProvider.go | 3 +- 5 files changed, 4 insertions(+), 714 deletions(-) delete mode 100644 pkg/diff/diff_test.go diff --git a/pkg/diff/diff.go b/pkg/diff/diff.go index 4c27dbd9ce..1e24402fa7 100644 --- a/pkg/diff/diff.go +++ b/pkg/diff/diff.go @@ -24,231 +24,14 @@ type Differ interface { ChangedGroups(existing []*models.RecordConfig) (map[models.RecordKey][]string, error) } -// // New is a constructor for a Differ. -// func New(dc *models.DomainConfig, extraValues ...func(*models.RecordConfig) map[string]string) Differ { -// return &differ{ -// dc: dc, -// extraValues: extraValues, - -// // compile IGNORE_NAME glob patterns -// compiledIgnoredNames: compileIgnoredNames(dc.IgnoredNames), - -// // compile IGNORE_TARGET glob patterns -// compiledIgnoredTargets: compileIgnoredTargets(dc.IgnoredTargets), -// } -// } - -// // An ignoredName must match both the name glob and one of the recordTypes in rTypes. If rTypes is empty, any -// // record type will match. -// type ignoredName struct { -// nameGlob glob.Glob -// rTypes []string -// } - type differ struct { - // dc *models.DomainConfig - // extraValues []func(*models.RecordConfig) map[string]string - - // compiledIgnoredNames []ignoredName - // compiledIgnoredTargets []glob.Glob } // get normalized content for record. target, ttl, mxprio, and specified metadata func (d *differ) content(r *models.RecordConfig) string { - - // get the extra values maps to add to the comparison. - var allMaps []map[string]string - // for _, f := range d.extraValues { - // valueMap := f(r) - // allMaps = append(allMaps, valueMap) - // } - - return r.ToDiffable(allMaps...) + return r.ToDiffable() } -// func apexException(rec *models.RecordConfig) bool { -// // Providers often add NS and SOA records at the apex. These -// // should not be included in certain checks. -// return (rec.Type == "NS" || rec.Type == "SOA") && rec.GetLabel() == "@" -// } - -// func ignoreNameException(rec *models.RecordConfig) bool { -// // People wanted it to be possible to disable this safety check. -// // Ok, here it is. You now have two risks: -// // 1. Two owners (DNSControl and some other entity) toggling a record between two settings. -// // 2. The other owner wiping all records at this label, which won't be noticed until the next time dnscontrol is run. -// //fmt.Printf("********** DEBUG IGNORE %v %v %q\n", rec.GetLabel(), rec.Type, rec.Metadata["ignore_name_disable_safety_check"]) -// // See https://github.com/StackExchange/dnscontrol/issues/1106 -// _, ok := rec.Metadata["ignore_name_disable_safety_check"] -// return ok -// } - -// func (d *differ) IncrementalDiff(existing []*models.RecordConfig) (unchanged, create, toDelete, modify Changeset, err error) { -// unchanged = Changeset{} -// create = Changeset{} -// toDelete = Changeset{} -// modify = Changeset{} -// desired := d.dc.Records - -// //fmt.Printf("********** DEBUG: STARTING IncrementalDiff\n") - -// // sort existing and desired by name - -// existingByNameAndType := map[models.RecordKey][]*models.RecordConfig{} -// desiredByNameAndType := map[models.RecordKey][]*models.RecordConfig{} - -// //fmt.Printf("********** DEBUG: existing list %+v\n", existing) - -// // Gather the existing records. Skip over any that should be ignored. -// for _, e := range existing { -// //fmt.Printf("********** DEBUG: existing %v %v %v\n", e.GetLabel(), e.Type, e.GetTargetCombined()) -// if d.matchIgnoredName(e.GetLabel(), e.Type) { -// //fmt.Printf("Ignoring record %s %s due to IGNORE_NAME\n", e.GetLabel(), e.Type) -// printer.Debugf("Ignoring record %s %s due to IGNORE_NAME\n", e.GetLabel(), e.Type) -// } else if d.matchIgnoredTarget(e.GetTargetField(), e.Type) { -// //fmt.Printf("Ignoring record %s %s due to IGNORE_TARGET\n", e.GetLabel(), e.Type) -// printer.Debugf("Ignoring record %s %s due to IGNORE_TARGET\n", e.GetLabel(), e.Type) -// } else { -// k := e.Key() -// existingByNameAndType[k] = append(existingByNameAndType[k], e) -// } -// } - -// // Review the desired records. If we're modifying one that should be ignored, that's an error. -// //fmt.Printf("********** DEBUG: desired list %+v\n", desired) -// for _, dr := range desired { -// //fmt.Printf("********** DEBUG: desired %v %v %v -- %v %v\n", dr.GetLabel(), dr.Type, dr.GetTargetCombined(), apexException(dr), d.matchIgnoredName(dr.GetLabel())) -// if d.matchIgnoredName(dr.GetLabel(), dr.Type) { -// //if !apexException(dr) || !ignoreNameException(dr) { -// if (!ignoreNameException(dr)) && (!apexException(dr)) { -// return nil, nil, nil, nil, fmt.Errorf("trying to update/add IGNORE_NAMEd record: %s %s", dr.GetLabel(), dr.Type) -// //} else { -// // fmt.Printf("********** DEBUG: desired EXCEPTION\n") -// } -// } else if d.matchIgnoredTarget(dr.GetTargetField(), dr.Type) { -// return nil, nil, nil, nil, fmt.Errorf("trying to update/add IGNORE_TARGETd record: %s %s", dr.GetLabel(), dr.Type) -// } else { -// k := dr.Key() -// desiredByNameAndType[k] = append(desiredByNameAndType[k], dr) -// } -// } -// // if NO_PURGE is set, just remove anything that is only in existing. -// if d.dc.KeepUnknown { -// for k := range existingByNameAndType { -// if _, ok := desiredByNameAndType[k]; !ok { -// printer.Debugf("Ignoring record set %s %s due to NO_PURGE\n", k.Type, k.NameFQDN) -// delete(existingByNameAndType, k) -// } -// } -// } -// // Look through existing records. This will give us changes and deletions and some additions. -// // Each iteration is only for a single type/name record set -// for key, existingRecords := range existingByNameAndType { -// desiredRecords := desiredByNameAndType[key] - -// // Very first, get rid of any identical records. Easy. -// for i := len(existingRecords) - 1; i >= 0; i-- { -// ex := existingRecords[i] -// for j, de := range desiredRecords { -// if d.content(de) != d.content(ex) { -// continue -// } -// unchanged = append(unchanged, Correlation{d, ex, de}) -// existingRecords = existingRecords[:i+copy(existingRecords[i:], existingRecords[i+1:])] -// desiredRecords = desiredRecords[:j+copy(desiredRecords[j:], desiredRecords[j+1:])] -// break -// } -// } - -// // Next, match by target. This will give the most natural modifications. -// for i := len(existingRecords) - 1; i >= 0; i-- { -// ex := existingRecords[i] -// for j, de := range desiredRecords { -// if de.GetTargetField() == ex.GetTargetField() { -// // two records share a target, but different content (ttl or metadata changes) -// modify = append(modify, Correlation{d, ex, de}) -// // remove from both slices by index -// existingRecords = existingRecords[:i+copy(existingRecords[i:], existingRecords[i+1:])] -// desiredRecords = desiredRecords[:j+copy(desiredRecords[j:], desiredRecords[j+1:])] -// break -// } -// } -// } - -// desiredLookup := map[string]*models.RecordConfig{} -// existingLookup := map[string]*models.RecordConfig{} -// // build index based on normalized content data -// for _, ex := range existingRecords { -// normalized := d.content(ex) -// //fmt.Printf("DEBUG: normalized: %v\n", normalized) -// // NB(tlim): Commenting this out. If the provider is returning -// // records that are exact duplicates, that's bad and against the -// // RFCs. However, we shouldn't error out. Instead, we should -// // continue so that we can delete them. Experience shows one -// // record will be deleted per iteration but at least the problem -// // will fix itself that way. Erroring out means it will require -// // manually fixing (going to the control panel, deleting -// // individual records, etc.) -// //if existingLookup[normalized] != nil { -// // return nil, nil, nil, nil, fmt.Errorf("DUPLICATE E_RECORD FOUND: %s %s", key, normalized) -// //} -// existingLookup[normalized] = ex -// } -// for _, de := range desiredRecords { -// normalized := d.content(de) -// if desiredLookup[normalized] != nil { -// return nil, nil, nil, nil, fmt.Errorf("DUPLICATE D_RECORD FOUND: %s %s", key, normalized) -// } -// desiredLookup[normalized] = de -// } -// // if a record is in both, it is unchanged -// for norm, ex := range existingLookup { -// if de, ok := desiredLookup[norm]; ok { -// unchanged = append(unchanged, Correlation{d, ex, de}) -// delete(existingLookup, norm) -// delete(desiredLookup, norm) -// } -// } -// // sort records by normalized text. Keeps behaviour deterministic -// existingStrings, desiredStrings := sortedKeys(existingLookup), sortedKeys(desiredLookup) -// // Modifications. Take 1 from each side. -// for len(desiredStrings) > 0 && len(existingStrings) > 0 { -// modify = append(modify, Correlation{d, existingLookup[existingStrings[0]], desiredLookup[desiredStrings[0]]}) -// existingStrings = existingStrings[1:] -// desiredStrings = desiredStrings[1:] -// } -// // If desired still has things they are additions -// for _, norm := range desiredStrings { -// rec := desiredLookup[norm] -// create = append(create, Correlation{d, nil, rec}) -// } -// // if found, but not desired, delete it -// for _, norm := range existingStrings { -// rec := existingLookup[norm] -// toDelete = append(toDelete, Correlation{d, rec, nil}) -// } -// // remove this set from the desired list to indicate we have processed it. -// delete(desiredByNameAndType, key) -// } - -// // any name/type sets not already processed are pure additions -// for name := range existingByNameAndType { -// delete(desiredByNameAndType, name) -// } -// for _, desiredList := range desiredByNameAndType { -// for _, rec := range desiredList { -// create = append(create, Correlation{d, nil, rec}) -// } -// } - -// // Sort the lists. This is purely cosmetic. -// sort.Slice(unchanged, func(i, j int) bool { return ChangesetLess(unchanged, i, j) }) -// sort.Slice(create, func(i, j int) bool { return ChangesetLess(create, i, j) }) -// sort.Slice(toDelete, func(i, j int) bool { return ChangesetLess(toDelete, i, j) }) - -// return -// } - // ChangesetLess returns true if c[i] < c[j]. func ChangesetLess(c Changeset, i, j int) bool { var a, b string @@ -285,50 +68,6 @@ func CorrectionLess(c []*models.Correction, i, j int) bool { return c[i].Msg < c[j].Msg } -// func (d *differ) ChangedGroups(existing []*models.RecordConfig) (map[models.RecordKey][]string, error) { -// changedKeys := map[models.RecordKey][]string{} -// _, create, toDelete, modify, err := d.IncrementalDiff(existing) -// if err != nil { -// return nil, err -// } -// for _, c := range create { -// changedKeys[c.Desired.Key()] = append(changedKeys[c.Desired.Key()], c.String()) -// } -// for _, d := range toDelete { -// changedKeys[d.Existing.Key()] = append(changedKeys[d.Existing.Key()], d.String()) -// } -// for _, m := range modify { -// changedKeys[m.Desired.Key()] = append(changedKeys[m.Desired.Key()], m.String()) -// } -// return changedKeys, nil -// } - -// // DebugKeyMapMap debug prints the results from ChangedGroups. -// func DebugKeyMapMap(note string, m map[models.RecordKey][]string) { -// // The output isn't pretty but it is useful. -// fmt.Println("DEBUG:", note) - -// // Extract the keys -// var keys []models.RecordKey -// for k := range m { -// keys = append(keys, k) -// } -// sort.SliceStable(keys, func(i, j int) bool { -// if keys[i].NameFQDN == keys[j].NameFQDN { -// return keys[i].Type < keys[j].Type -// } -// return keys[i].NameFQDN < keys[j].NameFQDN -// }) - -// // Pretty print the map: -// for _, k := range keys { -// fmt.Printf(" %v %v:\n", k.Type, k.NameFQDN) -// for _, s := range m[k] { -// fmt.Printf(" -- %q\n", s) -// } -// } -// } - func (c Correlation) String() string { if c.Existing == nil { return color.GreenString("+ CREATE %s %s %s", c.Desired.Type, c.Desired.GetLabelFQDN(), c.d.content(c.Desired)) @@ -338,85 +77,3 @@ func (c Correlation) String() string { } return color.YellowString("± MODIFY %s %s: (%s) -> (%s)", c.Existing.Type, c.Existing.GetLabelFQDN(), c.d.content(c.Existing), c.d.content(c.Desired)) } - -// func sortedKeys(m map[string]*models.RecordConfig) []string { -// s := []string{} -// for v := range m { -// s = append(s, v) -// } -// sort.Strings(s) -// return s -// } - -// var spaceCommaTokenizerRegexp = regexp.MustCompile(`\s*,\s*`) - -// func compileIgnoredNames(ignoredNames []*models.IgnoreName) []ignoredName { -// result := make([]ignoredName, 0, len(ignoredNames)) - -// for _, tst := range ignoredNames { -// g, err := glob.Compile(tst.Pattern, '.') -// if err != nil { -// panic(fmt.Sprintf("Failed to compile IGNORE_NAME pattern %q: %v", tst.Pattern, err)) -// } - -// t := []string{} -// if tst.Types != "" { -// t = spaceCommaTokenizerRegexp.Split(tst.Types, -1) -// } - -// result = append(result, ignoredName{nameGlob: g, rTypes: t}) -// } - -// return result -// } - -// func compileIgnoredTargets(ignoredTargets []*models.IgnoreTarget) []glob.Glob { -// result := make([]glob.Glob, 0, len(ignoredTargets)) - -// for _, tst := range ignoredTargets { -// if tst.Type != "CNAME" { -// panic(fmt.Sprintf("Invalid rType for IGNORE_TARGET %v", tst.Type)) -// } - -// g, err := glob.Compile(tst.Pattern, '.') -// if err != nil { -// panic(fmt.Sprintf("Failed to compile IGNORE_TARGET pattern %q: %v", tst, err)) -// } - -// result = append(result, g) -// } - -// return result -// } - -// func (d *differ) matchIgnoredName(name string, rType string) bool { -// for _, tst := range d.compiledIgnoredNames { -// //fmt.Printf("********** DEBUG: matchIgnoredName %q %q %v %v\n", name, rType, tst, tst.nameGlob.Match(name)) -// if tst.nameGlob.Match(name) { -// if tst.rTypes == nil { -// return true -// } - -// for _, rt := range tst.rTypes { -// if rt == "*" || rt == rType { -// return true -// } -// } -// } -// } -// return false -// } - -// func (d *differ) matchIgnoredTarget(target string, rType string) bool { -// if rType != "CNAME" { -// return false -// } - -// for _, tst := range d.compiledIgnoredTargets { -// if tst.Match(target) { -// return true -// } -// } - -// return false -// } diff --git a/pkg/diff/diff2compat.go b/pkg/diff/diff2compat.go index d747a9b7b4..6a75edada6 100644 --- a/pkg/diff/diff2compat.go +++ b/pkg/diff/diff2compat.go @@ -24,10 +24,7 @@ func NewCompat(dc *models.DomainConfig, extraValues ...func(*models.RecordConfig panic("extraValues not supported") } - // d := New(dc) return &differCompat{ - //OldDiffer: d.(*differ), - dc: dc, } } diff --git a/pkg/diff/diff_test.go b/pkg/diff/diff_test.go deleted file mode 100644 index 76969bcfdb..0000000000 --- a/pkg/diff/diff_test.go +++ /dev/null @@ -1,365 +0,0 @@ -package diff - -// func myRecord(s string) *models.RecordConfig { -// parts := strings.Split(s, " ") -// ttl, _ := strconv.ParseUint(parts[2], 10, 32) -// r := &models.RecordConfig{ -// Type: parts[1], -// TTL: uint32(ttl), -// Metadata: map[string]string{}, -// } -// r.SetLabel(parts[0], "example.com") -// r.SetTarget(parts[3]) -// return r -// } - -// func TestAdditionsOnly(t *testing.T) { -// desired := []*models.RecordConfig{ -// myRecord("@ A 1 1.2.3.4"), -// } -// existing := []*models.RecordConfig{} -// checkLengths(t, existing, desired, 0, 1, 0, 0) -// } - -// func TestDeletionsOnly(t *testing.T) { -// existing := []*models.RecordConfig{ -// myRecord("@ A 1 1.2.3.4"), -// } -// desired := []*models.RecordConfig{} -// checkLengths(t, existing, desired, 0, 0, 1, 0) -// } - -// func TestModification(t *testing.T) { -// existing := []*models.RecordConfig{ -// myRecord("www A 1 1.1.1.1"), -// myRecord("@ A 1 1.2.3.4"), -// } -// desired := []*models.RecordConfig{ -// myRecord("@ A 32 1.2.3.4"), -// myRecord("www A 1 1.1.1.1"), -// } -// un, _, _, mod := checkLengths(t, existing, desired, 1, 0, 0, 1) -// if un[0].Desired != desired[1] || un[0].Existing != existing[0] { -// t.Error("Expected unchanged records to be correlated") -// } -// if mod[0].Desired != desired[0] || mod[0].Existing != existing[1] { -// t.Errorf("Expected modified records to be correlated") -// } -// } - -// func TestUnchangedWithAddition(t *testing.T) { -// existing := []*models.RecordConfig{ -// myRecord("www A 1 1.1.1.1"), -// } -// desired := []*models.RecordConfig{ -// myRecord("www A 1 1.2.3.4"), -// myRecord("www A 1 1.1.1.1"), -// } -// un, _, _, _ := checkLengths(t, existing, desired, 1, 1, 0, 0) -// if un[0].Desired != desired[1] || un[0].Existing != existing[0] { -// t.Errorf("Expected unchanged records to be correlated") -// } -// } - -// // s stringifies a RecordConfig for testing purposes. -// func s(rc *models.RecordConfig) string { -// return fmt.Sprintf("%s %s %d %s", rc.GetLabel(), rc.Type, rc.TTL, rc.GetTargetCombined()) -// } - -// func TestOutOfOrderRecords(t *testing.T) { -// existing := []*models.RecordConfig{ -// myRecord("www A 1 1.1.1.1"), -// myRecord("www A 1 2.2.2.2"), -// myRecord("www A 1 3.3.3.3"), -// } -// desired := []*models.RecordConfig{ -// myRecord("www A 1 1.1.1.1"), -// myRecord("www A 1 2.2.2.2"), -// myRecord("www A 1 2.2.2.3"), -// myRecord("www A 10 3.3.3.3"), -// } -// _, _, _, mods := checkLengths(t, existing, desired, 2, 1, 0, 1) -// if s(mods[0].Desired) != s(desired[3]) || s(mods[0].Existing) != s(existing[2]) { -// t.Fatalf("Expected to match %s and %s, but matched %s and %s", s(existing[2]), s(desired[3]), s(mods[0].Existing), s(mods[0].Desired)) -// } -// } - -// func TestMxPrio(t *testing.T) { -// existing := []*models.RecordConfig{ -// myRecord("www MX 1 1.1.1.1"), -// } -// desired := []*models.RecordConfig{ -// myRecord("www MX 1 1.1.1.1"), -// } -// existing[0].MxPreference = 10 -// desired[0].MxPreference = 20 -// checkLengths(t, existing, desired, 0, 0, 0, 1) -// } - -// func TestTTLChange(t *testing.T) { -// existing := []*models.RecordConfig{ -// myRecord("www MX 1 1.1.1.1"), -// } -// desired := []*models.RecordConfig{ -// myRecord("www MX 10 1.1.1.1"), -// } -// checkLengths(t, existing, desired, 0, 0, 0, 1) -// } - -// func TestMetaChange(t *testing.T) { -// existing := []*models.RecordConfig{ -// myRecord("www MX 1 1.1.1.1"), -// } -// desired := []*models.RecordConfig{ -// myRecord("www MX 1 1.1.1.1"), -// } -// existing[0].Metadata["k"] = "aa" -// desired[0].Metadata["k"] = "bb" -// checkLengths(t, existing, desired, 1, 0, 0, 0) -// getMeta := func(r *models.RecordConfig) map[string]string { -// return map[string]string{ -// "k": r.Metadata["k"], -// } -// } -// checkLengths(t, existing, desired, 0, 0, 0, 1, getMeta) -// } -// -// func TestMetaOrdering(t *testing.T) { -// existing := []*models.RecordConfig{ -// myRecord("www MX 1 1.1.1.1"), -// } -// desired := []*models.RecordConfig{ -// myRecord("www MX 1 1.1.1.1"), -// } -// existing[0].Metadata["k"] = "aa" -// existing[0].Metadata["x"] = "cc" -// desired[0].Metadata["k"] = "aa" -// desired[0].Metadata["x"] = "cc" -// checkLengths(t, existing, desired, 1, 0, 0, 0) -// getMeta := func(r *models.RecordConfig) map[string]string { -// return map[string]string{ -// "k": r.Metadata["k"], -// } -// } -// checkLengths(t, existing, desired, 1, 0, 0, 0, getMeta) -// } - -// func checkLengths(t *testing.T, existing, desired []*models.RecordConfig, unCount, createCount, delCount, modCount int, valFuncs ...func(*models.RecordConfig) map[string]string) (un, cre, del, mod Changeset) { -// return checkLengthsWithKeepUnknown(t, existing, desired, unCount, createCount, delCount, modCount, false, valFuncs...) -// } - -// func checkLengthsWithKeepUnknown(t *testing.T, existing, desired []*models.RecordConfig, unCount, createCount, delCount, modCount int, keepUnknown bool, valFuncs ...func(*models.RecordConfig) map[string]string) (un, cre, del, mod Changeset) { -// return checkLengthsFull(t, existing, desired, unCount, createCount, delCount, modCount, keepUnknown, []*models.IgnoreName{}, nil, valFuncs...) -// } - -// func checkLengthsFull(t *testing.T, existing, desired []*models.RecordConfig, unCount, createCount, delCount, modCount int, keepUnknown bool, ignoredRecords []*models.IgnoreName, ignoredTargets []*models.IgnoreTarget, valFuncs ...func(*models.RecordConfig) map[string]string) (un, cre, del, mod Changeset) { -// dc := &models.DomainConfig{ -// Name: "example.com", -// Records: desired, -// KeepUnknown: keepUnknown, -// IgnoredNames: ignoredRecords, -// IgnoredTargets: ignoredTargets, -// } -// d := New(dc, valFuncs...) -// un, cre, del, mod, err := d.IncrementalDiff(existing) -// if err != nil { -// panic(err) -// } -// if len(un) != unCount { -// t.Errorf("Got %d unchanged records, but expected %d", len(un), unCount) -// } -// if len(cre) != createCount { -// t.Errorf("Got %d records to create, but expected %d", len(cre), createCount) -// } -// if len(del) != delCount { -// t.Errorf("Got %d records to delete, but expected %d", len(del), delCount) -// } -// if len(mod) != modCount { -// t.Errorf("Got %d records to modify, but expected %d", len(mod), modCount) -// } -// if t.Failed() { -// t.FailNow() -// } -// return -// } - -// func TestNoPurge(t *testing.T) { -// existing := []*models.RecordConfig{ -// myRecord("www MX 1 1.1.1.1"), -// myRecord("www MX 1 2.2.2.2"), -// myRecord("www2 MX 1 1.1.1.1"), -// } -// desired := []*models.RecordConfig{ -// myRecord("www MX 1 1.1.1.1"), -// } -// checkLengthsWithKeepUnknown(t, existing, desired, 1, 0, 1, 0, true) -// } - -// func TestIgnoredRecords(t *testing.T) { -// existing := []*models.RecordConfig{ -// myRecord("www1 A 1 1.1.1.1"), -// myRecord("www1 MX 1 1.1.1.1"), -// myRecord("www2 A 1 1.1.1.1"), -// myRecord("www2 CNAME 1 www"), -// myRecord("www2 MX 1 1.1.1.1"), -// myRecord("www3 MX 1 1.1.1.1"), -// } -// desired := []*models.RecordConfig{ -// myRecord("www3 MX 1 2.2.2.2"), -// } -// checkLengthsFull(t, existing, desired, 0, 0, 0, 1, false, -// []*models.IgnoreName{ -// {Pattern: "www1", Types: "*"}, -// {Pattern: "www2", Types: "A,MX, CNAME"}, -// }, -// nil, -// ) -// } - -// func TestModifyingIgnoredRecords(t *testing.T) { -// existing := []*models.RecordConfig{ -// myRecord("www1 MX 1 1.1.1.1"), -// myRecord("www2 MX 1 1.1.1.1"), -// myRecord("www3 MX 1 1.1.1.1"), -// } -// desired := []*models.RecordConfig{ -// myRecord("www2 MX 1 2.2.2.2"), -// } - -// defer func() { -// if r := recover(); r == nil { -// t.Errorf("should panic: modification of IGNOREd record") -// } -// }() - -// checkLengthsFull(t, existing, desired, 0, 0, 0, 1, false, -// []*models.IgnoreName{{Pattern: "www1", Types: "MX"}, {Pattern: "www2", Types: "*"}}, -// nil, -// ) -// } - -// func TestGlobIgnoredName(t *testing.T) { -// existing := []*models.RecordConfig{ -// myRecord("www1 MX 1 1.1.1.1"), -// myRecord("foo.www2 MX 1 1.1.1.1"), -// myRecord("foo.bar.www3 MX 1 1.1.1.1"), -// myRecord("www4 MX 1 1.1.1.1"), -// } -// desired := []*models.RecordConfig{ -// myRecord("www4 MX 1 2.2.2.2"), -// } -// checkLengthsFull(t, existing, desired, 0, 0, 0, 1, false, -// []*models.IgnoreName{ -// {Pattern: "www1", Types: "*"}, -// {Pattern: "*.www2", Types: "*"}, -// {Pattern: "**.www3", Types: "*"}, -// }, -// nil, -// ) -// } - -// func TestInvalidGlobIgnoredName(t *testing.T) { -// existing := []*models.RecordConfig{ -// myRecord("www1 MX 1 1.1.1.1"), -// myRecord("www2 MX 1 1.1.1.1"), -// myRecord("www3 MX 1 1.1.1.1"), -// } -// desired := []*models.RecordConfig{ -// myRecord("www4 MX 1 2.2.2.2"), -// } - -// defer func() { -// if r := recover(); r == nil { -// t.Errorf("should panic: invalid glob pattern for IGNORE_NAME") -// } -// }() - -// checkLengthsFull(t, existing, desired, 0, 1, 0, 0, false, -// []*models.IgnoreName{{Pattern: "www1"}, {Pattern: "*.www2"}, {Pattern: "[.www3"}}, -// nil, -// ) -// } - -// func TestGlobIgnoredTarget(t *testing.T) { -// existing := []*models.RecordConfig{ -// myRecord("www1 CNAME 1 ignoreme.com"), -// myRecord("foo.www2 MX 1 1.1.1.2"), -// myRecord("foo.bar.www3 MX 1 1.1.1.1"), -// myRecord("www4 MX 1 1.1.1.1"), -// } -// desired := []*models.RecordConfig{ -// myRecord("foo.www2 MX 1 1.1.1.2"), -// myRecord("foo.bar.www3 MX 1 1.1.1.1"), -// myRecord("www4 MX 1 2.2.2.2"), -// } -// checkLengthsFull(t, existing, desired, 2, 0, 0, 1, false, nil, []*models.IgnoreTarget{{Pattern: "ignoreme.com", Type: "CNAME"}}) -// } - -// func TestInvalidGlobIgnoredTarget(t *testing.T) { -// existing := []*models.RecordConfig{ -// myRecord("www1 MX 1 1.1.1.1"), -// myRecord("www2 MX 1 1.1.1.1"), -// myRecord("www3 MX 1 1.1.1.1"), -// } -// desired := []*models.RecordConfig{ -// myRecord("www4 MX 1 2.2.2.2"), -// } - -// defer func() { -// if r := recover(); r == nil { -// t.Errorf("should panic: invalid glob pattern for IGNORE_TARGET") -// } -// }() - -// checkLengthsFull(t, existing, desired, 0, 1, 0, 0, false, nil, []*models.IgnoreTarget{{Pattern: "[.www3", Type: "CNAME"}}) -// } - -// func TestInvalidTypeIgnoredTarget(t *testing.T) { -// existing := []*models.RecordConfig{ -// myRecord("www1 MX 1 1.1.1.1"), -// myRecord("www2 MX 1 1.1.1.1"), -// myRecord("www3 MX 1 1.1.1.1"), -// } -// desired := []*models.RecordConfig{ -// myRecord("www4 MX 1 2.2.2.2"), -// } - -// defer func() { -// if r := recover(); r == nil { -// t.Errorf("should panic: Invalid rType for IGNORE_TARGET A") -// } -// }() - -// checkLengthsFull(t, existing, desired, 0, 1, 0, 0, false, nil, []*models.IgnoreTarget{{Pattern: "1.1.1.1", Type: "A"}}) -// } -// -// // from https://github.com/StackExchange/dnscontrol/issues/552 -// func TestCaas(t *testing.T) { -// existing := []*models.RecordConfig{ -// myRecord("test CAA 1 1.1.1.1"), -// myRecord("test CAA 1 1.1.1.1"), -// myRecord("test CAA 1 1.1.1.1"), -// } -// desired := []*models.RecordConfig{ -// myRecord("test CAA 1 1.1.1.1"), -// myRecord("test CAA 1 1.1.1.1"), -// myRecord("test CAA 1 1.1.1.1"), -// } -// existing[0].SetTargetCAA(3, "issue", "letsencrypt.org.") -// existing[1].SetTargetCAA(3, "issue", "amazon.com.") -// existing[2].SetTargetCAA(3, "issuewild", "letsencrypt.org.") - -// // this will pass or fail depending on the ordering. Not ok. -// desired[0].SetTargetCAA(3, "issue", "letsencrypt.org.") -// desired[1].SetTargetCAA(3, "issue", "amazon.com.") -// desired[2].SetTargetCAA(3, "issuewild", "letsencrypt.org.") - -// checkLengthsFull(t, existing, desired, 3, 0, 0, 0, false, nil, nil) - -// // Make sure it passes with a different ordering. Not ok. -// desired[2].SetTargetCAA(3, "issue", "letsencrypt.org.") -// desired[1].SetTargetCAA(3, "issue", "amazon.com.") -// desired[0].SetTargetCAA(3, "issuewild", "letsencrypt.org.") - -// checkLengthsFull(t, existing, desired, 3, 0, 0, 0, false, nil, nil) -// } diff --git a/providers/hostingde/hostingdeProvider.go b/providers/hostingde/hostingdeProvider.go index 93a332e513..e15eeae80e 100644 --- a/providers/hostingde/hostingdeProvider.go +++ b/providers/hostingde/hostingdeProvider.go @@ -144,7 +144,7 @@ func (hp *hostingdeProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, // NOPURGE if dc.KeepUnknown { - del = []diff.Correlation{} + del = nil } // remove SOA record from corrections as it is handled separately diff --git a/providers/netcup/netcupProvider.go b/providers/netcup/netcupProvider.go index 936e09efcb..9d9fb31666 100644 --- a/providers/netcup/netcupProvider.go +++ b/providers/netcup/netcupProvider.go @@ -68,7 +68,8 @@ func (api *netcupProvider) GetNameservers(domain string) ([]*models.Nameserver, } // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. -func (api *netcupProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) (corrections []*models.Correction, err error) { +func (api *netcupProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, error) { + var corrections []*models.Correction domain := dc.Name // no need for txtutil.SplitSingleLongTxt in function GetDomainCorrections From dac3851c08a39bc606f9275065361fdec53529ea Mon Sep 17 00:00:00 2001 From: Tom Limoncelli Date: Sun, 1 Oct 2023 11:00:19 -0400 Subject: [PATCH 4/7] cleanups --- providers/azuredns/azureDnsProvider.go | 97 -------------------- providers/gandiv5/gandi_v5Provider.go | 3 +- providers/hetzner/hetznerProvider.go | 3 +- providers/hexonet/records.go | 3 +- providers/linode/linodeProvider.go | 5 +- providers/loopia/loopiaProvider.go | 4 +- providers/luadns/luadnsProvider.go | 3 +- providers/msdns/corrections.go | 3 +- providers/namedotcom/records.go | 3 +- providers/netlify/netlifyProvider.go | 4 +- providers/ns1/ns1Provider.go | 3 +- providers/packetframe/packetframeProvider.go | 4 +- providers/porkbun/porkbunProvider.go | 3 +- 13 files changed, 28 insertions(+), 110 deletions(-) diff --git a/providers/azuredns/azureDnsProvider.go b/providers/azuredns/azureDnsProvider.go index 8c400f1570..bc1c70998c 100644 --- a/providers/azuredns/azureDnsProvider.go +++ b/providers/azuredns/azureDnsProvider.go @@ -276,35 +276,6 @@ func (a *azurednsProvider) recordDelete(zoneName string, reckey models.RecordKey return err } -// func nativeToRecordType(recordType *string) (adns.RecordType, error) { -// recordTypeStripped := strings.TrimPrefix(*recordType, "Microsoft.Network/dnszones/") -// switch recordTypeStripped { -// case "A", "AZURE_ALIAS_A": -// return adns.RecordTypeA, nil -// case "AAAA", "AZURE_ALIAS_AAAA": -// return adns.RecordTypeAAAA, nil -// case "CAA": -// return adns.RecordTypeCAA, nil -// case "CNAME", "AZURE_ALIAS_CNAME": -// return adns.RecordTypeCNAME, nil -// case "MX": -// return adns.RecordTypeMX, nil -// case "NS": -// return adns.RecordTypeNS, nil -// case "PTR": -// return adns.RecordTypePTR, nil -// case "SRV": -// return adns.RecordTypeSRV, nil -// case "TXT": -// return adns.RecordTypeTXT, nil -// case "SOA": -// return adns.RecordTypeSOA, nil -// default: -// // Unimplemented type. Return adns.A as a decoy, but send an error. -// return adns.RecordTypeA, fmt.Errorf("nativeToRecordType rtype %v unimplemented", *recordType) -// } -// } - func nativeToRecordTypeDiff2(recordType *string) (adns.RecordType, error) { recordTypeStripped := strings.TrimPrefix(*recordType, "Microsoft.Network/dnszones/") switch recordTypeStripped { @@ -483,74 +454,6 @@ func nativeToRecords(set *adns.RecordSet, origin string) []*models.RecordConfig return results } -// func (a *azurednsProvider) recordToNative(recordKey models.RecordKey, recordConfig []*models.RecordConfig) (*adns.RecordSet, adns.RecordType, error) { -// recordSet := &adns.RecordSet{Type: to.StringPtr(recordKey.Type), Properties: &adns.RecordSetProperties{}} -// for _, rec := range recordConfig { -// switch recordKey.Type { -// case "A": -// if recordSet.Properties.ARecords == nil { -// recordSet.Properties.ARecords = []*adns.ARecord{} -// } -// recordSet.Properties.ARecords = append(recordSet.Properties.ARecords, &adns.ARecord{IPv4Address: to.StringPtr(rec.GetTargetField())}) -// case "AAAA": -// if recordSet.Properties.AaaaRecords == nil { -// recordSet.Properties.AaaaRecords = []*adns.AaaaRecord{} -// } -// recordSet.Properties.AaaaRecords = append(recordSet.Properties.AaaaRecords, &adns.AaaaRecord{IPv6Address: to.StringPtr(rec.GetTargetField())}) -// case "CNAME": -// recordSet.Properties.CnameRecord = &adns.CnameRecord{Cname: to.StringPtr(rec.GetTargetField())} -// case "NS": -// if recordSet.Properties.NsRecords == nil { -// recordSet.Properties.NsRecords = []*adns.NsRecord{} -// } -// recordSet.Properties.NsRecords = append(recordSet.Properties.NsRecords, &adns.NsRecord{Nsdname: to.StringPtr(rec.GetTargetField())}) -// case "PTR": -// if recordSet.Properties.PtrRecords == nil { -// recordSet.Properties.PtrRecords = []*adns.PtrRecord{} -// } -// recordSet.Properties.PtrRecords = append(recordSet.Properties.PtrRecords, &adns.PtrRecord{Ptrdname: to.StringPtr(rec.GetTargetField())}) -// case "TXT": -// if recordSet.Properties.TxtRecords == nil { -// recordSet.Properties.TxtRecords = []*adns.TxtRecord{} -// } -// // Empty TXT record needs to have no value set in it's properties -// if !(len(rec.TxtStrings) == 1 && rec.TxtStrings[0] == "") { -// var txts []*string -// for _, txt := range rec.TxtStrings { -// txts = append(txts, to.StringPtr(txt)) -// } -// recordSet.Properties.TxtRecords = append(recordSet.Properties.TxtRecords, &adns.TxtRecord{Value: txts}) -// } -// case "MX": -// if recordSet.Properties.MxRecords == nil { -// recordSet.Properties.MxRecords = []*adns.MxRecord{} -// } -// recordSet.Properties.MxRecords = append(recordSet.Properties.MxRecords, &adns.MxRecord{Exchange: to.StringPtr(rec.GetTargetField()), Preference: to.Int32Ptr(int32(rec.MxPreference))}) -// case "SRV": -// if recordSet.Properties.SrvRecords == nil { -// recordSet.Properties.SrvRecords = []*adns.SrvRecord{} -// } -// recordSet.Properties.SrvRecords = append(recordSet.Properties.SrvRecords, &adns.SrvRecord{Target: to.StringPtr(rec.GetTargetField()), Port: to.Int32Ptr(int32(rec.SrvPort)), Weight: to.Int32Ptr(int32(rec.SrvWeight)), Priority: to.Int32Ptr(int32(rec.SrvPriority))}) -// case "CAA": -// if recordSet.Properties.CaaRecords == nil { -// recordSet.Properties.CaaRecords = []*adns.CaaRecord{} -// } -// recordSet.Properties.CaaRecords = append(recordSet.Properties.CaaRecords, &adns.CaaRecord{Value: to.StringPtr(rec.GetTargetField()), Tag: to.StringPtr(rec.CaaTag), Flags: to.Int32Ptr(int32(rec.CaaFlag))}) -// case "AZURE_ALIAS_A", "AZURE_ALIAS_AAAA", "AZURE_ALIAS_CNAME": -// *recordSet.Type = rec.AzureAlias["type"] -// recordSet.Properties.TargetResource = &adns.SubResource{ID: to.StringPtr(rec.GetTargetField())} -// default: -// return nil, adns.RecordTypeA, fmt.Errorf("recordToNative rtype %v unimplemented", recordKey.Type) // ands.A is a placeholder -// } -// } - -// rt, err := nativeToRecordType(to.StringPtr(*recordSet.Type)) -// if err != nil { -// return nil, adns.RecordTypeA, err // adns.A is a placeholder -// } -// return recordSet, rt, nil -// } - // NOTE recordToNativeDiff2 is really "convert []RecordConfig to rrset". func (a *azurednsProvider) recordToNativeDiff2(recordKey models.RecordKey, recordConfig []*models.RecordConfig) (*adns.RecordSet, adns.RecordType, error) { diff --git a/providers/gandiv5/gandi_v5Provider.go b/providers/gandiv5/gandi_v5Provider.go index fd9f2bc3fb..d6fb5409fa 100644 --- a/providers/gandiv5/gandi_v5Provider.go +++ b/providers/gandiv5/gandi_v5Provider.go @@ -191,7 +191,8 @@ func PrepDesiredRecords(dc *models.DomainConfig) { } // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. -func (client *gandiv5Provider) GetZoneRecordsCorrections(dc *models.DomainConfig, existing models.Records) (corrections []*models.Correction, err error) { +func (client *gandiv5Provider) GetZoneRecordsCorrections(dc *models.DomainConfig, existing models.Records) ([]*models.Correction, error) { + var corrections []*models.Correction if client.debug { debugRecords("GenDC input", existing) } diff --git a/providers/hetzner/hetznerProvider.go b/providers/hetzner/hetznerProvider.go index ec85c1ecc2..d8566ffb14 100644 --- a/providers/hetzner/hetznerProvider.go +++ b/providers/hetzner/hetznerProvider.go @@ -69,7 +69,8 @@ func (api *hetznerProvider) EnsureZoneExists(domain string) error { } // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. -func (api *hetznerProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) (corrections []*models.Correction, err error) { +func (api *hetznerProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, error) { + var corrections []*models.Correction domain := dc.Name txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records diff --git a/providers/hexonet/records.go b/providers/hexonet/records.go index ea8a90f103..912feee5b2 100644 --- a/providers/hexonet/records.go +++ b/providers/hexonet/records.go @@ -57,7 +57,8 @@ func (n *HXClient) GetZoneRecords(domain string, meta map[string]string) (models } // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. -func (n *HXClient) GetZoneRecordsCorrections(dc *models.DomainConfig, actual models.Records) (corrections []*models.Correction, err error) { +func (n *HXClient) GetZoneRecordsCorrections(dc *models.DomainConfig, actual models.Records) ([]*models.Correction, error) { + var corrections []*models.Correction txtutil.SplitSingleLongTxt(dc.Records) diff --git a/providers/linode/linodeProvider.go b/providers/linode/linodeProvider.go index 62711815dd..107569238b 100644 --- a/providers/linode/linodeProvider.go +++ b/providers/linode/linodeProvider.go @@ -125,7 +125,8 @@ func (api *linodeProvider) GetZoneRecords(domain string, meta map[string]string) } // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. -func (api *linodeProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) (corrections []*models.Correction, err error) { +func (api *linodeProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, error) { + var corrections []*models.Correction // Linode doesn't allow selecting an arbitrary TTL, only a set of predefined values // We need to make sure we don't change it every time if it is as close as it's going to get // The documentation says that it will always round up to the next highest value: 300 -> 300, 301 -> 3600. @@ -135,7 +136,7 @@ func (api *linodeProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, ex } if api.domainIndex == nil { - if err = api.fetchDomainList(); err != nil { + if err := api.fetchDomainList(); err != nil { return nil, err } } diff --git a/providers/loopia/loopiaProvider.go b/providers/loopia/loopiaProvider.go index 3a504f0892..1275f32247 100644 --- a/providers/loopia/loopiaProvider.go +++ b/providers/loopia/loopiaProvider.go @@ -265,7 +265,9 @@ func gatherAffectedLabels(groups map[models.RecordKey][]string) (labels map[stri } // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. -func (c *APIClient) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) (corrections []*models.Correction, err error) { +func (c *APIClient) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, error) { + var corrections []*models.Correction + if c.Debug { debugRecords("GenerateZoneRecordsCorrections input:\n", existingRecords) } diff --git a/providers/luadns/luadnsProvider.go b/providers/luadns/luadnsProvider.go index f08d333623..2359175052 100644 --- a/providers/luadns/luadnsProvider.go +++ b/providers/luadns/luadnsProvider.go @@ -94,7 +94,8 @@ func (l *luadnsProvider) GetZoneRecords(domain string, meta map[string]string) ( } // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. -func (l *luadnsProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, records models.Records) (corrections []*models.Correction, err error) { +func (l *luadnsProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, records models.Records) ([]*models.Correction, error) { + var corrections []*models.Correction checkNS(dc) diff --git a/providers/msdns/corrections.go b/providers/msdns/corrections.go index 71ce9ba793..f58e2ba20c 100644 --- a/providers/msdns/corrections.go +++ b/providers/msdns/corrections.go @@ -9,7 +9,8 @@ import ( ) // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. -func (client *msdnsProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, foundRecords models.Records) (corrections []*models.Correction, err error) { +func (client *msdnsProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, foundRecords models.Records) ([]*models.Correction, error) { + var corrections []*models.Correction // Normalize models.PostProcessRecords(foundRecords) diff --git a/providers/namedotcom/records.go b/providers/namedotcom/records.go index 10f42d4867..398915c293 100644 --- a/providers/namedotcom/records.go +++ b/providers/namedotcom/records.go @@ -27,7 +27,8 @@ func (n *namedotcomProvider) GetZoneRecords(domain string, meta map[string]strin } // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. -func (n *namedotcomProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, actual models.Records) (corrections []*models.Correction, err error) { +func (n *namedotcomProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, actual models.Records) ([]*models.Correction, error) { + var corrections []*models.Correction checkNSModifications(dc) diff --git a/providers/netlify/netlifyProvider.go b/providers/netlify/netlifyProvider.go index eb7a4f0433..ae4a3f0688 100644 --- a/providers/netlify/netlifyProvider.go +++ b/providers/netlify/netlifyProvider.go @@ -139,7 +139,9 @@ func (n *netlifyProvider) GetZoneRecords(domain string, meta map[string]string) } // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. -func (n *netlifyProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, records models.Records) (corrections []*models.Correction, err error) { +func (n *netlifyProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, records models.Records) ([]*models.Correction, error) { + var corrections []*models.Correction + _, create, del, modify, err := diff.NewCompat(dc).IncrementalDiff(records) if err != nil { return nil, err diff --git a/providers/ns1/ns1Provider.go b/providers/ns1/ns1Provider.go index 47307421ef..59a048cc35 100644 --- a/providers/ns1/ns1Provider.go +++ b/providers/ns1/ns1Provider.go @@ -186,7 +186,8 @@ func (n *nsone) getDomainCorrectionsDNSSEC(domain, toggleDNSSEC string) *models. } // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. -func (n *nsone) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) (corrections []*models.Correction, err error) { +func (n *nsone) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, error) { + var corrections []*models.Correction domain := dc.Name // add DNSSEC-related corrections diff --git a/providers/packetframe/packetframeProvider.go b/providers/packetframe/packetframeProvider.go index cd9b4bde17..7ab111db39 100644 --- a/providers/packetframe/packetframeProvider.go +++ b/providers/packetframe/packetframeProvider.go @@ -100,7 +100,9 @@ func (api *packetframeProvider) GetZoneRecords(domain string, meta map[string]st } // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. -func (api *packetframeProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) (corrections []*models.Correction, err error) { +func (api *packetframeProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, error) { + var corrections []*models.Correction + zone, err := api.getZone(dc.Name) if err != nil { return nil, fmt.Errorf("no such zone %q in Packetframe account", dc.Name) diff --git a/providers/porkbun/porkbunProvider.go b/providers/porkbun/porkbunProvider.go index ec2327d9f2..18eec418e7 100644 --- a/providers/porkbun/porkbunProvider.go +++ b/providers/porkbun/porkbunProvider.go @@ -85,7 +85,8 @@ func (c *porkbunProvider) GetNameservers(domain string) ([]*models.Nameserver, e } // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. -func (c *porkbunProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) (corrections []*models.Correction, err error) { +func (c *porkbunProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, error) { + var corrections []*models.Correction // Block changes to NS records for base domain checkNSModifications(dc) From 402390c2275d7d64f0df37a486ea7f7676687ce7 Mon Sep 17 00:00:00 2001 From: Tom Limoncelli Date: Sun, 1 Oct 2023 11:54:12 -0400 Subject: [PATCH 5/7] Legacy systems can now process "report" messages --- pkg/diff/diff.go | 2 +- pkg/diff/diff2compat.go | 19 ++++---- providers/cloudns/cloudnsProvider.go | 5 +- providers/cscglobal/dns.go | 5 +- .../digitalocean/digitaloceanProvider.go | 5 +- providers/dnsimple/dnsimpleProvider.go | 9 ++-- providers/dnsmadeeasy/dnsMadeEasyProvider.go | 5 +- providers/domainnameshop/dns.go | 5 +- providers/exoscale/exoscaleProvider.go | 5 +- providers/gcloud/gcloudProvider.go | 10 ++-- providers/hetzner/hetznerProvider.go | 5 +- providers/hexonet/records.go | 6 +-- providers/hostingde/hostingdeProvider.go | 47 ++++++++++--------- providers/inwx/inwxProvider.go | 5 +- providers/linode/linodeProvider.go | 5 +- providers/loopia/loopiaProvider.go | 6 ++- providers/namecheap/namecheapProvider.go | 8 ++-- providers/namedotcom/records.go | 6 +-- providers/netcup/netcupProvider.go | 5 +- providers/netlify/netlifyProvider.go | 6 +-- providers/oracle/oracleProvider.go | 12 +++-- providers/packetframe/packetframeProvider.go | 6 +-- providers/rwth/dns.go | 5 +- providers/softlayer/softlayerProvider.go | 6 +-- 24 files changed, 109 insertions(+), 89 deletions(-) diff --git a/pkg/diff/diff.go b/pkg/diff/diff.go index 1e24402fa7..08806d8e3f 100644 --- a/pkg/diff/diff.go +++ b/pkg/diff/diff.go @@ -18,7 +18,7 @@ type Changeset []Correlation // Differ is an interface for computing the difference between two zones. type Differ interface { // IncrementalDiff performs a diff on a record-by-record basis, and returns a sets for which records need to be created, deleted, or modified. - IncrementalDiff(existing []*models.RecordConfig) (unchanged, create, toDelete, modify Changeset, err error) + IncrementalDiff(existing []*models.RecordConfig) (reportMsgs []string, create, toDelete, modify Changeset, err error) // ChangedGroups performs a diff more appropriate for providers with a "RecordSet" model, where all records with the same name and type are grouped. // Individual record changes are often not useful in such scenarios. Instead we return a map of record keys to a list of change descriptions within that group. ChangedGroups(existing []*models.RecordConfig) (map[models.RecordKey][]string, error) diff --git a/pkg/diff/diff2compat.go b/pkg/diff/diff2compat.go index 6a75edada6..2a926832fe 100644 --- a/pkg/diff/diff2compat.go +++ b/pkg/diff/diff2compat.go @@ -45,7 +45,7 @@ type differCompat struct { // - The NewCompat() feature `extraValues` is not supported. That // parameter must be set to nil. If you use that feature, consider // one of the pkg/diff2/By*() functions. -func (d *differCompat) IncrementalDiff(existing []*models.RecordConfig) (unchanged, toCreate, toDelete, toModify Changeset, err error) { +func (d *differCompat) IncrementalDiff(existing []*models.RecordConfig) (reportMsgs []string, toCreate, toDelete, toModify Changeset, err error) { instructions, err := diff2.ByRecord(existing, d.dc, nil) if err != nil { return nil, nil, nil, nil, err @@ -55,15 +55,7 @@ func (d *differCompat) IncrementalDiff(existing []*models.RecordConfig) (unchang cor := Correlation{} switch inst.Type { case diff2.REPORT: - // Sadly the NewCompat function doesn't have an equivalent. We - // just output the messages now. - fmt.Print("INFO: ") - fmt.Println(inst.MsgsJoined) - - // TODO(tlim): When diff1 is deleted, IncremtntalDiff should add a - // parameter to list the REPORT messages. It can also eliminate the - // first parameter (existing) since nobody uses that in the diff2 - // world. + reportMsgs = append(reportMsgs, inst.Msgs...) case diff2.CREATE: cor.Desired = inst.New[0] toCreate = append(toCreate, cor) @@ -82,6 +74,13 @@ func (d *differCompat) IncrementalDiff(existing []*models.RecordConfig) (unchang return } +func GenerateMessageCorrections(msgs []string) (corrections []*models.Correction) { + for _, msg := range msgs { + corrections = append(corrections, &models.Correction{Msg: msg}) + } + return +} + // ChangedGroups provides the same results as IncrementalDiff but grouped by key. func (d *differCompat) ChangedGroups(existing []*models.RecordConfig) (map[models.RecordKey][]string, error) { changedKeys := map[models.RecordKey][]string{} diff --git a/providers/cloudns/cloudnsProvider.go b/providers/cloudns/cloudnsProvider.go index 05471312a7..ab79ef1cb4 100644 --- a/providers/cloudns/cloudnsProvider.go +++ b/providers/cloudns/cloudnsProvider.go @@ -128,11 +128,12 @@ func (c *cloudnsProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, exi record.TTL = fixTTL(record.TTL) } - var corrections []*models.Correction - _, create, del, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords) + toReport, create, del, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords) if err != nil { return nil, err } + // Start corrections with the reports + corrections := diff.GenerateMessageCorrections(toReport) // Deletes first so changing type works etc. for _, m := range del { diff --git a/providers/cscglobal/dns.go b/providers/cscglobal/dns.go index ba41ff72a5..4cf3cb5175 100644 --- a/providers/cscglobal/dns.go +++ b/providers/cscglobal/dns.go @@ -78,11 +78,12 @@ func (client *providerClient) GetNameservers(domain string) ([]*models.Nameserve func (client *providerClient) GetZoneRecordsCorrections(dc *models.DomainConfig, foundRecords models.Records) ([]*models.Correction, error) { //txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records - var corrections []*models.Correction - _, creates, dels, modifications, err := diff.NewCompat(dc).IncrementalDiff(foundRecords) + toReport, creates, dels, modifications, err := diff.NewCompat(dc).IncrementalDiff(foundRecords) if err != nil { return nil, err } + // Start corrections with the reports + corrections := diff.GenerateMessageCorrections(toReport) // CSCGlobal has a unique API. A list of edits is sent in one API // call. Edits aren't permitted if an existing edit is being diff --git a/providers/digitalocean/digitaloceanProvider.go b/providers/digitalocean/digitaloceanProvider.go index 9ba96a3a3b..62ec0d7dd9 100644 --- a/providers/digitalocean/digitaloceanProvider.go +++ b/providers/digitalocean/digitaloceanProvider.go @@ -172,11 +172,12 @@ func (api *digitaloceanProvider) GetZoneRecordsCorrections(dc *models.DomainConf ctx := context.Background() - var corrections []*models.Correction - _, toCreate, toDelete, toModify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords) + toReport, toCreate, toDelete, toModify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords) if err != nil { return nil, err } + // Start corrections with the reports + corrections := diff.GenerateMessageCorrections(toReport) // Deletes first so changing type works etc. for _, m := range toDelete { diff --git a/providers/dnsimple/dnsimpleProvider.go b/providers/dnsimple/dnsimpleProvider.go index 3789792afd..e1fc4277eb 100644 --- a/providers/dnsimple/dnsimpleProvider.go +++ b/providers/dnsimple/dnsimpleProvider.go @@ -139,20 +139,21 @@ func (c *dnsimpleProvider) GetZoneRecords(domain string, meta map[string]string) } func (c *dnsimpleProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, actual models.Records) ([]*models.Correction, error) { - var corrections []*models.Correction - removeOtherApexNS(dc) dnssecFixes, err := c.getDNSSECCorrections(dc) if err != nil { return nil, err } - corrections = append(corrections, dnssecFixes...) - _, create, del, modify, err := diff.NewCompat(dc).IncrementalDiff(actual) + toReport, create, del, modify, err := diff.NewCompat(dc).IncrementalDiff(actual) if err != nil { return nil, err } + // Start corrections with the reports + corrections := diff.GenerateMessageCorrections(toReport) + // Next dnsSec fixes + corrections = append(corrections, dnssecFixes...) for _, del := range del { rec := del.Existing.Original.(dnsimpleapi.ZoneRecord) diff --git a/providers/dnsmadeeasy/dnsMadeEasyProvider.go b/providers/dnsmadeeasy/dnsMadeEasyProvider.go index 72afc913b8..3e6f1eb31e 100644 --- a/providers/dnsmadeeasy/dnsMadeEasyProvider.go +++ b/providers/dnsmadeeasy/dnsMadeEasyProvider.go @@ -117,11 +117,12 @@ func (api *dnsMadeEasyProvider) GetZoneRecordsCorrections(dc *models.DomainConfi } } - var corrections []*models.Correction - _, create, del, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords) + toReport, create, del, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords) if err != nil { return nil, err } + // Start corrections with the reports + corrections := diff.GenerateMessageCorrections(toReport) var deleteRecordIds []int deleteDescription := []string{"Batch deletion of records:"} diff --git a/providers/domainnameshop/dns.go b/providers/domainnameshop/dns.go index 436356456a..e7619c11d8 100644 --- a/providers/domainnameshop/dns.go +++ b/providers/domainnameshop/dns.go @@ -39,11 +39,12 @@ func (api *domainNameShopProvider) GetZoneRecordsCorrections(dc *models.DomainCo record.TTL = fixTTL(record.TTL) } - var corrections []*models.Correction - _, create, delete, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords) + toReport, create, delete, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords) if err != nil { return nil, err } + // Start corrections with the reports + corrections := diff.GenerateMessageCorrections(toReport) // Delete record for _, r := range delete { diff --git a/providers/exoscale/exoscaleProvider.go b/providers/exoscale/exoscaleProvider.go index b689f5a148..491e3e7be3 100644 --- a/providers/exoscale/exoscaleProvider.go +++ b/providers/exoscale/exoscaleProvider.go @@ -190,11 +190,12 @@ func (c *exoscaleProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, ex } domainID := *domain.ID - var corrections []*models.Correction - _, create, toDelete, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords) + toReport, create, toDelete, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords) if err != nil { return nil, err } + // Start corrections with the reports + corrections := diff.GenerateMessageCorrections(toReport) for _, del := range toDelete { record := del.Existing.Original.(*egoscale.DNSDomainRecord) diff --git a/providers/gcloud/gcloudProvider.go b/providers/gcloud/gcloudProvider.go index c20cf4a84e..8fd387ee20 100644 --- a/providers/gcloud/gcloudProvider.go +++ b/providers/gcloud/gcloudProvider.go @@ -259,7 +259,6 @@ type correctionValues struct { // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. func (g *gcloudProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, error) { - txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records oldRRs, ok := g.oldRRsMap[dc.Name] @@ -272,16 +271,20 @@ func (g *gcloudProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, exis } // first collect keys that have changed - _, create, delete, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords) + toReport, create, toDelete, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords) if err != nil { return nil, fmt.Errorf("incdiff error: %w", err) } + // Start corrections with the reports + corrections := diff.GenerateMessageCorrections(toReport) + + // Now generate all other corrections changedKeys := map[key]string{} for _, c := range create { changedKeys[keyForRec(c.Desired)] = fmt.Sprintln(c) } - for _, d := range delete { + for _, d := range toDelete { changedKeys[keyForRec(d.Existing)] = fmt.Sprintln(d) } for _, m := range modify { @@ -366,7 +369,6 @@ func (g *gcloudProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, exis } // create a Correction for each gdns.Change // that needs to be executed - corrections := []*models.Correction{} makeCorrection := func(chg *gdns.Change, msgs string) { runChange := func() error { retry: diff --git a/providers/hetzner/hetznerProvider.go b/providers/hetzner/hetznerProvider.go index d8566ffb14..fbb411252c 100644 --- a/providers/hetzner/hetznerProvider.go +++ b/providers/hetzner/hetznerProvider.go @@ -70,15 +70,16 @@ func (api *hetznerProvider) EnsureZoneExists(domain string) error { // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. func (api *hetznerProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, error) { - var corrections []*models.Correction domain := dc.Name txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records - _, create, del, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords) + toReport, create, del, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords) if err != nil { return nil, err } + // Start corrections with the reports + corrections := diff.GenerateMessageCorrections(toReport) z, err := api.getZone(domain) if err != nil { diff --git a/providers/hexonet/records.go b/providers/hexonet/records.go index 912feee5b2..3cb1846db7 100644 --- a/providers/hexonet/records.go +++ b/providers/hexonet/records.go @@ -58,14 +58,14 @@ func (n *HXClient) GetZoneRecords(domain string, meta map[string]string) (models // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. func (n *HXClient) GetZoneRecordsCorrections(dc *models.DomainConfig, actual models.Records) ([]*models.Correction, error) { - var corrections []*models.Correction - txtutil.SplitSingleLongTxt(dc.Records) - _, create, del, mod, err := diff.NewCompat(dc).IncrementalDiff(actual) + toReport, create, del, mod, err := diff.NewCompat(dc).IncrementalDiff(actual) if err != nil { return nil, err } + // Start corrections with the reports + corrections := diff.GenerateMessageCorrections(toReport) buf := &bytes.Buffer{} // Print a list of changes. Generate an actual change that is the zone diff --git a/providers/hostingde/hostingdeProvider.go b/providers/hostingde/hostingdeProvider.go index e15eeae80e..9410ee4b41 100644 --- a/providers/hostingde/hostingdeProvider.go +++ b/providers/hostingde/hostingdeProvider.go @@ -137,10 +137,12 @@ func (hp *hostingdeProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, return nil, err } - _, create, del, mod, err := diff.NewCompat(dc).IncrementalDiff(records) + toReport, create, del, mod, err := diff.NewCompat(dc).IncrementalDiff(records) if err != nil { return nil, err } + // Start corrections with the reports + corrections := diff.GenerateMessageCorrections(toReport) // NOPURGE if dc.KeepUnknown { @@ -261,31 +263,30 @@ func (hp *hostingdeProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, return nil, nil } - corrections := []*models.Correction{ - { - Msg: fmt.Sprintf("\n%s", strings.Join(msg, "\n")), - F: func() error { - for i := 0; i < 10; i++ { - err := hp.updateZone(&zone.ZoneConfig, DNSSecOptions, create, del, mod) - if err == nil { - return nil - } - // Code:10205 indicates the zone is currently blocked due to a running zone update. - if !strings.Contains(err.Error(), "Code:10205") { - return err - } - - // Exponential back-off retry. - // Base of 1.8 seemed like a good trade-off, retrying for approximately 45 seconds. - time.Sleep(time.Duration(math.Pow(1.8, float64(i))) * 100 * time.Millisecond) + corrections = append(corrections, &models.Correction{ + Msg: fmt.Sprintf("\n%s", strings.Join(msg, "\n")), + F: func() error { + for i := 0; i < 10; i++ { + err := hp.updateZone(&zone.ZoneConfig, DNSSecOptions, create, del, mod) + if err == nil { + return nil } - return fmt.Errorf("retry exhaustion: zone blocked for 10 attempts") - }, + // Code:10205 indicates the zone is currently blocked due to a running zone update. + if !strings.Contains(err.Error(), "Code:10205") { + return err + } + + // Exponential back-off retry. + // Base of 1.8 seemed like a good trade-off, retrying for approximately 45 seconds. + time.Sleep(time.Duration(math.Pow(1.8, float64(i))) * 100 * time.Millisecond) + } + return fmt.Errorf("retry exhaustion: zone blocked for 10 attempts") }, - } + }, + ) if removeDNSSecEntries != nil { - correction := models.Correction{ + correction := &models.Correction{ Msg: "Removing AutoDNSSEC Keys from Domain", F: func() error { err := hp.dnsSecKeyModify(dc.Name, nil, removeDNSSecEntries) @@ -295,7 +296,7 @@ func (hp *hostingdeProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, return nil }, } - corrections = append(corrections, &correction) + corrections = append(corrections, correction) } return corrections, nil diff --git a/providers/inwx/inwxProvider.go b/providers/inwx/inwxProvider.go index cb78c74999..189a1ce406 100644 --- a/providers/inwx/inwxProvider.go +++ b/providers/inwx/inwxProvider.go @@ -235,11 +235,12 @@ func (api *inwxAPI) GetZoneRecordsCorrections(dc *models.DomainConfig, foundReco return nil, err } - var corrections []*models.Correction - _, create, del, mod, err := diff.NewCompat(dc).IncrementalDiff(foundRecords) + toReport, create, del, mod, err := diff.NewCompat(dc).IncrementalDiff(foundRecords) if err != nil { return nil, err } + // Start corrections with the reports + corrections := diff.GenerateMessageCorrections(toReport) for _, d := range create { des := d.Desired diff --git a/providers/linode/linodeProvider.go b/providers/linode/linodeProvider.go index 107569238b..de3b4cd523 100644 --- a/providers/linode/linodeProvider.go +++ b/providers/linode/linodeProvider.go @@ -126,7 +126,6 @@ func (api *linodeProvider) GetZoneRecords(domain string, meta map[string]string) // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. func (api *linodeProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, error) { - var corrections []*models.Correction // Linode doesn't allow selecting an arbitrary TTL, only a set of predefined values // We need to make sure we don't change it every time if it is as close as it's going to get // The documentation says that it will always round up to the next highest value: 300 -> 300, 301 -> 3600. @@ -145,10 +144,12 @@ func (api *linodeProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, ex return nil, fmt.Errorf("'%s' not a zone in Linode account", dc.Name) } - _, create, del, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords) + toReport, create, del, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords) if err != nil { return nil, err } + // Start corrections with the reports + corrections := diff.GenerateMessageCorrections(toReport) // Deletes first so changing type works etc. for _, m := range del { diff --git a/providers/loopia/loopiaProvider.go b/providers/loopia/loopiaProvider.go index 1275f32247..cfa68c60e6 100644 --- a/providers/loopia/loopiaProvider.go +++ b/providers/loopia/loopiaProvider.go @@ -266,7 +266,6 @@ func gatherAffectedLabels(groups map[models.RecordKey][]string) (labels map[stri // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. func (c *APIClient) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, error) { - var corrections []*models.Correction if c.Debug { debugRecords("GenerateZoneRecordsCorrections input:\n", existingRecords) @@ -278,7 +277,7 @@ func (c *APIClient) GetZoneRecordsCorrections(dc *models.DomainConfig, existingR var keysToUpdate map[models.RecordKey][]string differ := diff.NewCompat(dc) - _, create, del, modify, err := differ.IncrementalDiff(existingRecords) + toReport, create, del, modify, err := differ.IncrementalDiff(existingRecords) if err != nil { return nil, err } @@ -287,6 +286,9 @@ func (c *APIClient) GetZoneRecordsCorrections(dc *models.DomainConfig, existingR return nil, err } + // Start corrections with the reports + corrections := diff.GenerateMessageCorrections(toReport) + for _, d := range create { // fmt.Printf("a creation: subdomain: %+v, existingfqdn: %+v \n", d.Desired.Name, d.Desired.NameFQDN) des := d.Desired diff --git a/providers/namecheap/namecheapProvider.go b/providers/namecheap/namecheapProvider.go index d7f23ae9c9..ddbc2a0273 100644 --- a/providers/namecheap/namecheapProvider.go +++ b/providers/namecheap/namecheapProvider.go @@ -244,10 +244,12 @@ func (n *namecheapProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, a return true }) - _, create, delete, modify, err := diff.NewCompat(dc).IncrementalDiff(actual) + toReport, create, delete, modify, err := diff.NewCompat(dc).IncrementalDiff(actual) if err != nil { return nil, err } + // Start corrections with the reports + corrections := diff.GenerateMessageCorrections(toReport) // because namecheap doesn't have selective create, delete, modify, // we bundle them all up to send at once. We *do* want to see the @@ -264,11 +266,9 @@ func (n *namecheapProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, a desc = append(desc, "\n"+i.String()) } - msg := fmt.Sprintf("GENERATE_ZONE: %s (%d records)%s", dc.Name, len(dc.Records), desc) - var corrections []*models.Correction - // only create corrections if there are changes if len(desc) > 0 { + msg := fmt.Sprintf("GENERATE_ZONE: %s (%d records)%s", dc.Name, len(dc.Records), desc) corrections = append(corrections, &models.Correction{ Msg: msg, diff --git a/providers/namedotcom/records.go b/providers/namedotcom/records.go index 398915c293..b9edb4ef8e 100644 --- a/providers/namedotcom/records.go +++ b/providers/namedotcom/records.go @@ -28,8 +28,6 @@ func (n *namedotcomProvider) GetZoneRecords(domain string, meta map[string]strin // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. func (n *namedotcomProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, actual models.Records) ([]*models.Correction, error) { - var corrections []*models.Correction - checkNSModifications(dc) for _, rec := range dc.Records { @@ -38,10 +36,12 @@ func (n *namedotcomProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, } } - _, create, del, mod, err := diff.NewCompat(dc).IncrementalDiff(actual) + toReport, create, del, mod, err := diff.NewCompat(dc).IncrementalDiff(actual) if err != nil { return nil, err } + // Start corrections with the reports + corrections := diff.GenerateMessageCorrections(toReport) for _, d := range del { rec := d.Existing.Original.(*namecom.Record) diff --git a/providers/netcup/netcupProvider.go b/providers/netcup/netcupProvider.go index 9d9fb31666..e55901772d 100644 --- a/providers/netcup/netcupProvider.go +++ b/providers/netcup/netcupProvider.go @@ -69,7 +69,6 @@ func (api *netcupProvider) GetNameservers(domain string) ([]*models.Nameserver, // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. func (api *netcupProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, error) { - var corrections []*models.Correction domain := dc.Name // no need for txtutil.SplitSingleLongTxt in function GetDomainCorrections @@ -89,10 +88,12 @@ func (api *netcupProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, ex } dc.Records = newRecords - _, create, del, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords) + toReport, create, del, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords) if err != nil { return nil, err } + // Start corrections with the reports + corrections := diff.GenerateMessageCorrections(toReport) // Deletes first so changing type works etc. for _, m := range del { diff --git a/providers/netlify/netlifyProvider.go b/providers/netlify/netlifyProvider.go index ae4a3f0688..2bdb1c1e8e 100644 --- a/providers/netlify/netlifyProvider.go +++ b/providers/netlify/netlifyProvider.go @@ -140,12 +140,12 @@ func (n *netlifyProvider) GetZoneRecords(domain string, meta map[string]string) // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. func (n *netlifyProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, records models.Records) ([]*models.Correction, error) { - var corrections []*models.Correction - - _, create, del, modify, err := diff.NewCompat(dc).IncrementalDiff(records) + toReport, create, del, modify, err := diff.NewCompat(dc).IncrementalDiff(records) if err != nil { return nil, err } + // Start corrections with the reports + corrections := diff.GenerateMessageCorrections(toReport) zone, err := n.getZone(dc.Name) if err != nil { diff --git a/providers/oracle/oracleProvider.go b/providers/oracle/oracleProvider.go index a9b0c078d5..892e6cee8a 100644 --- a/providers/oracle/oracleProvider.go +++ b/providers/oracle/oracleProvider.go @@ -224,10 +224,12 @@ func (o *oracleProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, exis } } - _, create, dels, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords) + toReport, create, dels, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords) if err != nil { return nil, err } + // Start corrections with the reports + corrections := diff.GenerateMessageCorrections(toReport) /* Oracle's API doesn't have a way to update an existing record. @@ -266,15 +268,17 @@ func (o *oracleProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, exis desc = desc[:len(desc)-1] } + // There were corrections. Send them as one big batch: if len(createRecords) > 0 || len(deleteRecords) > 0 { - return []*models.Correction{{ + corrections = append(corrections, &models.Correction{ Msg: desc, F: func() error { return o.patch(createRecords, deleteRecords, dc.Name) }, - }}, nil + }) } - return []*models.Correction{}, nil + + return corrections, nil } func (o *oracleProvider) patch(createRecords, deleteRecords models.Records, domain string) error { diff --git a/providers/packetframe/packetframeProvider.go b/providers/packetframe/packetframeProvider.go index 7ab111db39..82e5565c25 100644 --- a/providers/packetframe/packetframeProvider.go +++ b/providers/packetframe/packetframeProvider.go @@ -101,17 +101,17 @@ func (api *packetframeProvider) GetZoneRecords(domain string, meta map[string]st // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. func (api *packetframeProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, error) { - var corrections []*models.Correction - zone, err := api.getZone(dc.Name) if err != nil { return nil, fmt.Errorf("no such zone %q in Packetframe account", dc.Name) } - _, create, dels, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords) + toReport, create, dels, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords) if err != nil { return nil, err } + // Start corrections with the reports + corrections := diff.GenerateMessageCorrections(toReport) for _, m := range create { req, err := toReq(zone.ID, dc, m.Desired) diff --git a/providers/rwth/dns.go b/providers/rwth/dns.go index 645be5e5a4..88b31e6e7e 100644 --- a/providers/rwth/dns.go +++ b/providers/rwth/dns.go @@ -34,11 +34,12 @@ func (api *rwthProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, exis txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records domain := dc.Name - var corrections []*models.Correction - _, create, del, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords) + toReport, create, del, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords) if err != nil { return nil, err } + // Start corrections with the reports + corrections := diff.GenerateMessageCorrections(toReport) for _, d := range create { des := d.Desired diff --git a/providers/softlayer/softlayerProvider.go b/providers/softlayer/softlayerProvider.go index 326dc81b50..4238302b89 100644 --- a/providers/softlayer/softlayerProvider.go +++ b/providers/softlayer/softlayerProvider.go @@ -76,17 +76,17 @@ func (s *softlayerProvider) GetZoneRecords(domainName string, meta map[string]st // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. func (s *softlayerProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, actual models.Records) ([]*models.Correction, error) { - var corrections []*models.Correction - domain, err := s.getDomain(&dc.Name) if err != nil { return nil, err } - _, create, deletes, modify, err := diff.NewCompat(dc).IncrementalDiff(actual) + toReport, create, deletes, modify, err := diff.NewCompat(dc).IncrementalDiff(actual) if err != nil { return nil, err } + // Start corrections with the reports + corrections := diff.GenerateMessageCorrections(toReport) for _, del := range deletes { existing := del.Existing.Original.(datatypes.Dns_Domain_ResourceRecord) From 5f729ce7fc8e4fa1023be460a6dad7a4011bb0e2 Mon Sep 17 00:00:00 2001 From: Tom Limoncelli Date: Sun, 1 Oct 2023 11:56:50 -0400 Subject: [PATCH 6/7] Cleanup comments --- pkg/diff/diff2compat.go | 12 ++---------- pkg/printer/printer.go | 3 --- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/pkg/diff/diff2compat.go b/pkg/diff/diff2compat.go index 2a926832fe..11ca62cbf6 100644 --- a/pkg/diff/diff2compat.go +++ b/pkg/diff/diff2compat.go @@ -35,16 +35,8 @@ type differCompat struct { dc *models.DomainConfig } -// IncrementalDiff generates the diff using the pkg/diff2 code. -// NOTE: While this attempts to be backwards compatible, it does not -// support all features of the old system: -// - The IncrementalDiff() `unchanged` return value is always empty. -// Most providers ignore this return value. If a provider depends on -// that result, please consider one of the pkg/diff2/By*() functions -// instead. (ByZone() is likely to be what you need) -// - The NewCompat() feature `extraValues` is not supported. That -// parameter must be set to nil. If you use that feature, consider -// one of the pkg/diff2/By*() functions. +// IncrementalDiff usees pkg/diff2 to generate output compatible with systems +// still using NewCompat(). func (d *differCompat) IncrementalDiff(existing []*models.RecordConfig) (reportMsgs []string, toCreate, toDelete, toModify Changeset, err error) { instructions, err := diff2.ByRecord(existing, d.dc, nil) if err != nil { diff --git a/pkg/printer/printer.go b/pkg/printer/printer.go index 1a7aa3251e..f23fd91005 100644 --- a/pkg/printer/printer.go +++ b/pkg/printer/printer.go @@ -92,10 +92,7 @@ func (c ConsolePrinter) PrintCorrection(i int, correction *models.Correction) { // PrintReport is called to print/format each non-mutating correction (diff2.REPORT). func (c ConsolePrinter) PrintReport(i int, correction *models.Correction) { - // when diff1 is eliminated: fmt.Fprintf(c.Writer, "INFO#%d: %s\n", i+1, correction.Msg) - // When diff1 exists: - //fmt.Fprintf(c.Writer, "INFO: %s\n", correction.Msg) } // PromptToRun prompts the user to see if they want to execute a correction. From c53e2ea6d714d292481de899584d62ba5f751a43 Mon Sep 17 00:00:00 2001 From: Tom Limoncelli Date: Sun, 1 Oct 2023 12:05:48 -0400 Subject: [PATCH 7/7] ChangedGroups gets toReport too --- pkg/diff/diff.go | 2 +- pkg/diff/diff2compat.go | 8 ++++---- providers/akamaiedgedns/akamaiEdgeDnsProvider.go | 5 +++-- providers/desec/desecProvider.go | 7 ++++--- providers/loopia/loopiaProvider.go | 8 ++++---- 5 files changed, 16 insertions(+), 14 deletions(-) diff --git a/pkg/diff/diff.go b/pkg/diff/diff.go index 08806d8e3f..16412ae2f7 100644 --- a/pkg/diff/diff.go +++ b/pkg/diff/diff.go @@ -21,7 +21,7 @@ type Differ interface { IncrementalDiff(existing []*models.RecordConfig) (reportMsgs []string, create, toDelete, modify Changeset, err error) // ChangedGroups performs a diff more appropriate for providers with a "RecordSet" model, where all records with the same name and type are grouped. // Individual record changes are often not useful in such scenarios. Instead we return a map of record keys to a list of change descriptions within that group. - ChangedGroups(existing []*models.RecordConfig) (map[models.RecordKey][]string, error) + ChangedGroups(existing []*models.RecordConfig) (map[models.RecordKey][]string, []string, error) } type differ struct { diff --git a/pkg/diff/diff2compat.go b/pkg/diff/diff2compat.go index 11ca62cbf6..b9f617da30 100644 --- a/pkg/diff/diff2compat.go +++ b/pkg/diff/diff2compat.go @@ -74,11 +74,11 @@ func GenerateMessageCorrections(msgs []string) (corrections []*models.Correction } // ChangedGroups provides the same results as IncrementalDiff but grouped by key. -func (d *differCompat) ChangedGroups(existing []*models.RecordConfig) (map[models.RecordKey][]string, error) { +func (d *differCompat) ChangedGroups(existing []*models.RecordConfig) (map[models.RecordKey][]string, []string, error) { changedKeys := map[models.RecordKey][]string{} - _, toCreate, toDelete, toModify, err := d.IncrementalDiff(existing) + toReport, toCreate, toDelete, toModify, err := d.IncrementalDiff(existing) if err != nil { - return nil, err + return nil, nil, err } for _, c := range toCreate { changedKeys[c.Desired.Key()] = append(changedKeys[c.Desired.Key()], c.String()) @@ -89,5 +89,5 @@ func (d *differCompat) ChangedGroups(existing []*models.RecordConfig) (map[model for _, m := range toModify { changedKeys[m.Desired.Key()] = append(changedKeys[m.Desired.Key()], m.String()) } - return changedKeys, nil + return changedKeys, toReport, nil } diff --git a/providers/akamaiedgedns/akamaiEdgeDnsProvider.go b/providers/akamaiedgedns/akamaiEdgeDnsProvider.go index 82d05430f5..a42fe4b523 100644 --- a/providers/akamaiedgedns/akamaiEdgeDnsProvider.go +++ b/providers/akamaiedgedns/akamaiEdgeDnsProvider.go @@ -108,11 +108,12 @@ func (a *edgeDNSProvider) EnsureZoneExists(domain string) error { func (a *edgeDNSProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, error) { txtutil.SplitSingleLongTxt(existingRecords) - var corrections []*models.Correction - keysToUpdate, err := diff.NewCompat(dc).ChangedGroups(existingRecords) + keysToUpdate, toReport, err := diff.NewCompat(dc).ChangedGroups(existingRecords) if err != nil { return nil, err } + // Start corrections with the reports + corrections := diff.GenerateMessageCorrections(toReport) existingRecordsMap := make(map[models.RecordKey][]*models.RecordConfig) for _, r := range existingRecords { diff --git a/providers/desec/desecProvider.go b/providers/desec/desecProvider.go index caf69526e6..d67fa73cb4 100644 --- a/providers/desec/desecProvider.go +++ b/providers/desec/desecProvider.go @@ -163,13 +163,14 @@ func (c *desecProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, exist c.mutex.Unlock() PrepDesiredRecords(dc, minTTL) - var corrections []*models.Correction - keysToUpdate, err := diff.NewCompat(dc).ChangedGroups(existing) + keysToUpdate, toReport, err := diff.NewCompat(dc).ChangedGroups(existing) if err != nil { return nil, err } + // Start corrections with the reports + corrections := diff.GenerateMessageCorrections(toReport) - if len(keysToUpdate) == 0 { + if len(corrections) == 0 && len(keysToUpdate) == 0 { return nil, nil } diff --git a/providers/loopia/loopiaProvider.go b/providers/loopia/loopiaProvider.go index cfa68c60e6..96815a9b52 100644 --- a/providers/loopia/loopiaProvider.go +++ b/providers/loopia/loopiaProvider.go @@ -281,14 +281,14 @@ func (c *APIClient) GetZoneRecordsCorrections(dc *models.DomainConfig, existingR if err != nil { return nil, err } - keysToUpdate, err = differ.ChangedGroups(existingRecords) + // Start corrections with the reports + corrections := diff.GenerateMessageCorrections(toReport) + + keysToUpdate, _, err = differ.ChangedGroups(existingRecords) if err != nil { return nil, err } - // Start corrections with the reports - corrections := diff.GenerateMessageCorrections(toReport) - for _, d := range create { // fmt.Printf("a creation: subdomain: %+v, existingfqdn: %+v \n", d.Desired.Name, d.Desired.NameFQDN) des := d.Desired