1
0
mirror of https://github.com/StackExchange/dnscontrol.git synced 2024-05-11 05:55:12 +00:00

BUG: Some DNS zones are downloaded twice (#2120)

Signed-off-by: Amelia Aronsohn <squirrel@wearing.black>
Co-authored-by: Tom Limoncelli <tal@whatexit.org>
Co-authored-by: Grégoire Henry <hnrgrgr@users.noreply.github.com>
Co-authored-by: Amelia Aronsohn <squirrel@wearing.black>
Co-authored-by: Kai Schwarz <kschwarz@hexonet.net>
Co-authored-by: Asif Nawaz <asif.nawaz@centralnic.com>
Co-authored-by: imlonghao <git@imlonghao.com>
Co-authored-by: Will Power <1619102+willpower232@users.noreply.github.com>
This commit is contained in:
Tom Limoncelli
2023-04-14 15:22:23 -04:00
committed by GitHub
parent 61559f6a96
commit 60470a3886
56 changed files with 994 additions and 1186 deletions

View File

@@ -2,7 +2,6 @@ package commands
import (
"fmt"
"log"
"os"
"strings"
"sync"
@@ -15,6 +14,7 @@ import (
"github.com/StackExchange/dnscontrol/v3/pkg/normalize"
"github.com/StackExchange/dnscontrol/v3/pkg/notifications"
"github.com/StackExchange/dnscontrol/v3/pkg/printer"
"github.com/StackExchange/dnscontrol/v3/pkg/zonerecs"
"github.com/StackExchange/dnscontrol/v3/providers"
"github.com/urfave/cli/v2"
"golang.org/x/exp/slices"
@@ -139,6 +139,7 @@ func run(args PreviewArgs, push bool, interactive bool, out printer.CLI) error {
var wg sync.WaitGroup
wg.Add(len(cfg.Domains))
// For each domain in dnsconfig.js...
for _, domain := range cfg.Domains {
// Run preview or push operations per domain as anonymous function, in preparation for the later use of goroutines.
// For now running this code is still sequential.
@@ -150,8 +151,16 @@ func run(args PreviewArgs, push bool, interactive bool, out printer.CLI) error {
return
}
err = domain.Punycode()
if err != nil {
return
}
// Correct the domain...
out.StartDomain(domain.UniqueName)
var providersWithExistingZone []*models.DNSProviderInstance
/// For each DSP...
for _, provider := range domain.DNSProviderInstances {
if !args.NoPopulate {
// preview run: check if zone is already there, if not print a warning
@@ -164,8 +173,8 @@ func run(args PreviewArgs, push bool, interactive bool, out printer.CLI) error {
aceZoneName, _ := idna.ToASCII(domain.Name)
if !slices.Contains(zones, aceZoneName) {
out.Warnf("DEBUG: zones: %v\n", zones)
out.Warnf("DEBUG: Name: %v\n", domain.Name)
//out.Warnf("DEBUG: zones: %v\n", zones)
//out.Warnf("DEBUG: Name: %v\n", domain.Name)
out.Warnf("Zone '%s' does not exist in the '%s' profile and will be added automatically.\n", domain.Name, provider.Name)
continue // continue with next provider, as we can not determine corrections without an existing zone
@@ -181,6 +190,8 @@ func run(args PreviewArgs, push bool, interactive bool, out printer.CLI) error {
providersWithExistingZone = append(providersWithExistingZone, provider)
}
// Correct the registrar...
nsList, err := nameservers.DetermineNameserversForProviders(domain, providersWithExistingZone)
if err != nil {
out.Errorf("ERROR: %s", err.Error())
@@ -190,20 +201,14 @@ func run(args PreviewArgs, push bool, interactive bool, out printer.CLI) error {
nameservers.AddNSRecords(domain)
for _, provider := range providersWithExistingZone {
dc, err := domain.Copy()
if err != nil {
out.Errorf("ERROR: %s", err.Error())
return
}
shouldrun := args.shouldRunProvider(provider.Name, dc)
shouldrun := args.shouldRunProvider(provider.Name, domain)
out.StartDNSProvider(provider.Name, !shouldrun)
if !shouldrun {
continue
}
/// This is where we should audit?
corrections, err := provider.Driver.GetDomainCorrections(dc)
corrections, err := zonerecs.CorrectZoneRecords(provider.Driver, domain)
out.EndProvider(provider.Name, len(corrections), err)
if err != nil {
anyErrors = true
@@ -212,6 +217,8 @@ func run(args PreviewArgs, push bool, interactive bool, out printer.CLI) error {
totalCorrections += len(corrections)
anyErrors = printOrRunCorrections(domain.Name, provider.Name, corrections, out, push, interactive, notifier) || anyErrors
}
//
run := args.shouldRunProvider(domain.RegistrarName, domain)
out.StartRegistrar(domain.RegistrarName, !run)
if !run {
@@ -221,11 +228,8 @@ func run(args PreviewArgs, push bool, interactive bool, out printer.CLI) error {
out.Warnf("No nameservers declared; skipping registrar. Add {no_ns:'true'} to force.\n")
return
}
dc, err := domain.Copy()
if err != nil {
log.Fatal(err)
}
corrections, err := domain.RegistrarInstance.Driver.GetRegistrarCorrections(dc)
corrections, err := domain.RegistrarInstance.Driver.GetRegistrarCorrections(domain)
out.EndProvider(domain.RegistrarName, len(corrections), err)
if err != nil {
anyErrors = true

View File

@@ -66,10 +66,12 @@ And update `creds.json` accordingly:
```json
{
"axfrddns-a": {
"TYPE": "AXFRDDNS",
"transfer-key": "hmac-sha256:transfer-key-id:Base64EncodedSecret=",
"update-key": "hmac-sha256:update-key-id:AnotherSecret="
},
"axfrddns-b": {
"TYPE": "AXFRDDNS",
"transfer-key": "hmac-sha512:transfer-key-id-B:SmallSecret=",
"update-key": "hmac-sha512:update-key-id-B:YetAnotherSecret="
}
@@ -132,6 +134,16 @@ the following error message:
Please consider adding default `nameservers` or an explicit `master` in `creds.json`.
```
### Buggy DNS servers regarding CNAME updates
When modifying a CNAME record, or when replacing an A record by a
CNAME one in a single batched DDNS update, some DNS servers
(e.g. Knot) will incorrectly reject the update. For this particular
case, you might set the option `buggy-cname = "yes"` in `creds.json`.
The changes will then be split in two DDNS updates, applied
successively by the server. This will allow Knot to successfully apply
the changes, but you will loose the atomic-update property.
## Server configuration examples

View File

@@ -14,41 +14,33 @@ you designate someone else as the maintainer). More details
I'll ignore all the small stuff and get to the point.
A provider's `GetDomainCorrections()` function is the workhorse
of the provider. It is what gets called by `dnscontrol preview`
A typical provider implements 3 methods and DNSControl takes care of the rest:
* GetZoneRecords() -- Download the list of DNS records and return them as a list of RecordConfig structs.
* GetZoneRecordsCorrections() -- Generate a list of corrections.
* GetNameservers() -- Query the API and return the list of parent nameservers.
These three functions are all that's needed for `dnscontrol preview`
and `dnscontrol push`.
How does a provider's `GetDomainCorrections()` function work?
The goal of `GetZoneRecords()` is to download all the DNS records,
convert them to `models.RecordConfig` format, and return them as one big list
(`models.Records`).
The goal of `GetDomainCorrections()` is to return a list of
The goal of `GetZoneRecordsCorrections()` is to return a list of
corrections. Each correction is a text string describing the change
("Delete CNAME record foo") and a function that, if called, will
make the change (i.e. call the API and delete record foo). Preview
mode simply prints the text strings. `dnscontrol push` prints the
make the change (i.e. call the API and delete record foo). `dnscontrol preview`
simply prints the text strings. `dnscontrol push` prints the
strings and calls the functions. Because of how Go's functions work,
the function will have everything it needs to make the change.
Pretty cool, eh?
So how does `GetDomainCorrections()` work?
First, some terminology: The DNS records specified in the `dnsconfig.js`
file are called the "desired" records. The DNS records stored at
the DNS service provider are called the "existing" records.
Every provider does the same basic process. The function
`GetDomainCorrections()` is called with a list of the desired DNS
records (`dc.Records`). It then contacts the provider's API and
gathers the existing records. It converts the existing records into
a list of `*models.RecordConfig`.
Now that it has the desired and existing records in the appropriate
format, `differ.IncrementalDiff(existingRecords)` is called and
does all the hard work of understanding the DNS records and figuring
out what changes need to be made. It generates lists of adds,
deletes, and changes.
`GetDomainCorrections()` then generates the list of `models.Corrections()`
and returns. DNSControl takes care of the rest.
Calculating the difference between existing and desired is difficult. Luckily
the work is done for you. `GetZoneRecordsCorrections()` calls a a function in
the `pkg/diff2` module that generates a list of changes (usually an ADD,
CHANGE, or DELETE) that can easily be turned into the API calls mentioned
previously.
So, what does all this mean?
@@ -61,11 +53,6 @@ If you are new to Go, there are plenty of providers you can copy
from. In fact, many non-Go programmers
[have learned Go by contributing to DNSControl](https://everythingsysadmin.com/2017/08/go-get-up-to-speed.html).
Oh, and what if the API simply requires that the entire zonefile be uploaded
every time? We still generate the text descriptions of the changes (so that
`dnscontrol preview` looks nice) but the functions are just no-ops, except
for one that uploads the new zonefile.
Now that you understand the general process, here are the details.
## Step 1: General advice
@@ -80,12 +67,44 @@ was confusing so we can update this document with advice for future
authors (or even better, update [this document](https://github.com/StackExchange/dnscontrol/blob/master/documentation/writing-providers.md)
yourself.)
## NOTE: diff2
We are in the process of changing how providers work. Sadly this document
hasn't been fully updated yet.
We are in the process of changing all providers from using `pkg/diff` to
`pkg/diff2`. diff2 is much easier to use as it does all the hard work for you.
Providers are easier to write, there's less code for you to write, and fewer
chances to make mistakes.
New providers only need to implement diff2. Older providers are implemented
both ways, with a flag (`--diff2`) enabling the newer code. Soon the new code
will become the default, then the old code will be removed.
The file `pkg/diff2/diff2.go` has instructions about how to use the new diff2 system.
You can also do `grep diff2.By providers/*/*.go` to find providers that use
the new system.
Each DNS provider's API is different. Some update one DNS record at a time.
Others, the only change they permit is to upload the entire zone even if only one record changed!
Others are somewhere in between: all records at a label must be updated at once, or all records
in a RecordSet (the label + rType). diff2 provides functions for all of these situations:
diff2.ByRecord() -- Updates are done one DNS record at a time. New records are added. Changes and deletes refer to an ID assigned to the record by the provider.
diff2.ByLabel() -- Updates are done for an entire label. Adds and changes are done by sending one or more records that will appear at that label (i.e. www.example.com). Deletes delete all records at that label.
diff2.ByRecordSet() -- Similar to ByLabel() but updates are done on the label+type level. If www.example.com has 2 A records and 2 MX records,
## Step 2: Pick a base provider
Pick a similar provider as your base. Providers basically fall
into three general categories:
NOTE: diff2 changes this. For now, you can simply run `grep diff2.By providers/*/*.go` to see which
providers use ByZone, ByLabel, ByRecord, ByRecordSet and pick a similar provider to copy from.
* **zone:** The API requires you to upload the entire zone every time. (BIND, NAMECHEAP).
* **incremental-record:** The API lets you add/change/delete individual DNS records. (CLOUDFLARE, DNSIMPLE, NAMEDOTCOM, GCLOUD, HEXONET)
* **incremental-label:** Like incremental-record, but if there are

View File

@@ -15,6 +15,7 @@ import (
"github.com/StackExchange/dnscontrol/v3/pkg/diff2"
"github.com/StackExchange/dnscontrol/v3/pkg/nameservers"
"github.com/StackExchange/dnscontrol/v3/pkg/normalize"
"github.com/StackExchange/dnscontrol/v3/pkg/zonerecs"
"github.com/StackExchange/dnscontrol/v3/providers"
_ "github.com/StackExchange/dnscontrol/v3/providers/_all"
"github.com/StackExchange/dnscontrol/v3/providers/cloudflare"
@@ -200,6 +201,12 @@ func makeChanges(t *testing.T, prv providers.DNSServiceProvider, dc *models.Doma
//}
dom.Records = append(dom.Records, &rc)
}
if *providerToRun == "AXFRDDNS" {
// Bind will refuse a DDNS update when the resulting zone
// contains a NS record without an associated address
// records (A or AAAA)
dom.Records = append(dom.Records, a("ns."+domainName+".", "9.8.7.6"))
}
dom.IgnoredNames = tst.IgnoredNames
dom.IgnoredTargets = tst.IgnoredTargets
models.PostProcessRecords(dom.Records)
@@ -211,7 +218,7 @@ func makeChanges(t *testing.T, prv providers.DNSServiceProvider, dc *models.Doma
}
// get and run corrections for first time
corrections, err := prv.GetDomainCorrections(dom)
corrections, err := zonerecs.CorrectZoneRecords(prv, dom)
if err != nil {
t.Fatal(fmt.Errorf("runTests: %w", err))
}
@@ -234,7 +241,7 @@ func makeChanges(t *testing.T, prv providers.DNSServiceProvider, dc *models.Doma
}
// run a second time and expect zero corrections
corrections, err = prv.GetDomainCorrections(dom2)
corrections, err = zonerecs.CorrectZoneRecords(prv, dom2)
if err != nil {
t.Fatal(err)
}
@@ -328,7 +335,8 @@ func TestDualProviders(t *testing.T) {
// clear everything
run := func() {
dom, _ := dc.Copy()
cs, err := p.GetDomainCorrections(dom)
cs, err := zonerecs.CorrectZoneRecords(p, dom)
if err != nil {
t.Fatal(err)
}
@@ -350,7 +358,7 @@ func TestDualProviders(t *testing.T) {
run()
// run again to make sure no corrections
t.Log("Running again to ensure stability")
cs, err := p.GetDomainCorrections(dc)
cs, err := zonerecs.CorrectZoneRecords(p, dc)
if err != nil {
t.Fatal(err)
}

View File

@@ -22,7 +22,8 @@
"master": "$AXFRDDNS_MASTER",
"nameservers": "ns.example.com",
"transfer-key": "$AXFRDDNS_TRANSFER_KEY",
"update-key": "$AXFRDDNS_UPDATE_KEY"
"update-key": "$AXFRDDNS_UPDATE_KEY",
"buggy-cname": "$AXFRDDNS_BUGGY_CNAME"
},
"AZURE_DNS": {
"ClientID": "$AZURE_DNS_CLIENT_ID",

View File

@@ -4,7 +4,7 @@ package models
type DNSProvider interface {
GetNameservers(domain string) ([]*Nameserver, error)
GetZoneRecords(domain string) (Records, error)
GetDomainCorrections(dc *DomainConfig) ([]*Correction, error)
GetZoneRecordsCorrections(dc *DomainConfig, existing Records) ([]*Correction, error)
}
// Registrar is an interface for Registrar plug-ins.

View File

@@ -555,6 +555,7 @@ func (recs Records) GroupedByFQDN() ([]string, map[string]Records) {
}
// PostProcessRecords does any post-processing of the downloaded DNS records.
// Deprecated. zonerecords.CorrectZoneRecords() calls Downcase directly.
func PostProcessRecords(recs []*RecordConfig) {
Downcase(recs)
}

View File

@@ -15,6 +15,7 @@ import (
"github.com/StackExchange/dnscontrol/v3/models"
"github.com/StackExchange/dnscontrol/v3/pkg/nameservers"
"github.com/StackExchange/dnscontrol/v3/pkg/notifications"
"github.com/StackExchange/dnscontrol/v3/pkg/zonerecs"
"github.com/go-acme/lego/certcrypto"
"github.com/go-acme/lego/certificate"
"github.com/go-acme/lego/challenge"
@@ -275,7 +276,7 @@ func (c *certManager) getCorrections(d *models.DomainConfig) ([]*models.Correcti
if err != nil {
return nil, err
}
corrections, err := p.Driver.GetDomainCorrections(dc)
corrections, err := zonerecs.CorrectZoneRecords(p.Driver, dc)
if err != nil {
return nil, err
}

View File

@@ -5,6 +5,7 @@ import "github.com/StackExchange/dnscontrol/v3/models"
// SplitSingleLongTxt finds TXT records with a single long string and splits it
// into 255-octet chunks. This is used by providers that, when a user specifies
// one long TXT string, split it into smaller strings behind the scenes.
// This should be called from GetZoneRecordsCorrections().
func SplitSingleLongTxt(records []*models.RecordConfig) {
for _, rc := range records {
if rc.HasFormatIdenticalToTXT() {

View File

@@ -0,0 +1,34 @@
package zonerecs
import (
"github.com/StackExchange/dnscontrol/v3/models"
)
// CorrectZoneRecords calls both GetZoneRecords, does any
// post-processing, and then calls GetZoneRecordsCorrections. The
// name sucks because all the good names were taken.
func CorrectZoneRecords(driver models.DNSProvider, dc *models.DomainConfig) ([]*models.Correction, error) {
existingRecords, err := driver.GetZoneRecords(dc.Name)
if err != nil {
return nil, err
}
// downcase
models.Downcase(existingRecords)
// Copy dc so that any corrections code that wants to
// modify the records may. For example, if the provider only
// supports certain TTL values, it will adjust the ones in
// dc.Records.
dc, err = dc.Copy()
if err != nil {
return nil, err
}
// punycode
dc.Punycode()
// FIXME(tlim) It is a waste to PunyCode every iteration.
// This should be moved to where the JavaScript is processed.
return driver.GetZoneRecordsCorrections(dc, existingRecords)
}

View File

@@ -105,26 +105,13 @@ func (a *edgeDNSProvider) EnsureZoneExists(domain string) error {
return createZone(domain, a.contractID, a.groupID)
}
// GetDomainCorrections return a list of corrections. Each correction is a text string describing the change
// and a function that, if called, will make the change.
// “dnscontrol preview” simply prints the text strings.
// "dnscontrol push" prints the strings and calls the functions.
func (a *edgeDNSProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
err := dc.Punycode()
if err != nil {
return nil, err
}
existingRecords, err := getRecords(dc.Name)
if err != nil {
return nil, err
}
models.PostProcessRecords(existingRecords)
txtutil.SplitSingleLongTxt(dc.Records)
// GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records.
func (a *edgeDNSProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, error) {
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 {

View File

@@ -67,31 +67,12 @@ func New(settings map[string]string, _ json.RawMessage) (providers.DNSServicePro
return api, nil
}
// GetDomainCorrections returns the corrections for a domain.
func (api *autoDNSProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
var changes []*models.RecordConfig
dc, err := dc.Copy()
if err != nil {
return nil, err
}
err = dc.Punycode()
if err != nil {
return nil, err
}
// GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records.
func (api *autoDNSProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, error) {
domain := dc.Name
// Get existing records
existingRecords, err := api.GetZoneRecords(domain)
if err != nil {
return nil, err
}
// Normalize
models.PostProcessRecords(existingRecords)
txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records
var changes []*models.RecordConfig
var corrections []*models.Correction
if !diff2.EnableDiff2 {

View File

@@ -29,6 +29,7 @@ import (
"github.com/StackExchange/dnscontrol/v3/pkg/printer"
"github.com/StackExchange/dnscontrol/v3/pkg/txtutil"
"github.com/StackExchange/dnscontrol/v3/providers"
"github.com/fatih/color"
"github.com/miekg/dns"
)
@@ -56,13 +57,15 @@ var features = providers.DocumentationNotes{
// axfrddnsProvider stores the client info for the provider.
type axfrddnsProvider struct {
rand *rand.Rand
master string
updateMode string
transferMode string
nameservers []*models.Nameserver
transferKey *Key
updateKey *Key
rand *rand.Rand
master string
updateMode string
transferMode string
nameservers []*models.Nameserver
transferKey *Key
updateKey *Key
hasDnssecRecords bool
serverHasBuggyCNAME bool
}
func initAxfrDdns(config map[string]string, providermeta json.RawMessage) (providers.DNSServiceProvider, error) {
@@ -132,6 +135,12 @@ func initAxfrDdns(config map[string]string, providermeta json.RawMessage) (provi
if err != nil {
return nil, err
}
switch strings.ToLower(strings.TrimSpace(config["buggy-cname"])) {
case "yes", "true":
api.serverHasBuggyCNAME = true
default:
api.serverHasBuggyCNAME = false
}
for key := range config {
switch key {
case "master",
@@ -139,7 +148,9 @@ func initAxfrDdns(config map[string]string, providermeta json.RawMessage) (provi
"update-key",
"transfer-key",
"update-mode",
"transfer-mode":
"transfer-mode",
"domain",
"TYPE":
continue
default:
printer.Printf("[Warning] AXFRDDNS: unknown key in `creds.json` (%s)\n", key)
@@ -301,7 +312,7 @@ func (c *axfrddnsProvider) GetZoneRecords(domain string) (models.Records, error)
if len(foundRecords) >= 1 && foundRecords[len(foundRecords)-1].Type == "SOA" {
// The SOA is sent two times: as the first and the last record
// See section 2.2 of RFC5936
// See section 2.2 of RFC5936. We remove the later one.
foundRecords = foundRecords[:len(foundRecords)-1]
}
@@ -309,153 +320,309 @@ func (c *axfrddnsProvider) GetZoneRecords(domain string) (models.Records, error)
foundRecords = append(foundRecords, foundDNSSecRecords)
}
return foundRecords, nil
}
// GetDomainCorrections returns a list of corrections to update a domain.
func (c *axfrddnsProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
dc.Punycode()
foundRecords, err := c.GetZoneRecords(dc.Name)
if err != nil {
return nil, err
}
if len(foundRecords) >= 1 && foundRecords[0].Type == "SOA" {
// Ignoring the SOA, others providers don't manage it either.
foundRecords = foundRecords[1:]
}
hasDnssecRecords := false
c.hasDnssecRecords = false
if len(foundRecords) >= 1 {
last := foundRecords[len(foundRecords)-1]
if last.Type == "TXT" &&
last.Name == dnssecDummyLabel &&
len(last.TxtStrings) == 1 &&
last.TxtStrings[0] == dnssecDummyTxt {
hasDnssecRecords = true
c.hasDnssecRecords = true
foundRecords = foundRecords[0:(len(foundRecords) - 1)]
}
}
return foundRecords, nil
}
// BuildCorrection return a Correction for a given set of DDNS update and the corresponding message.
func (c *axfrddnsProvider) BuildCorrection(dc *models.DomainConfig, msgs []string, update *dns.Msg) *models.Correction {
return &models.Correction{
Msg: fmt.Sprintf("DDNS UPDATES to '%s' (primary master: '%s'). Changes:\n%s", dc.Name, c.master, strings.Join(msgs, "\n")),
F: func() error {
client := new(dns.Client)
client.Net = c.updateMode
client.Timeout = dnsTimeout
if c.updateKey != nil {
client.TsigSecret =
map[string]string{c.updateKey.id: c.updateKey.secret}
update.SetTsig(c.updateKey.id, c.updateKey.algo, 300, time.Now().Unix())
if c.updateKey.algo == dns.HmacMD5 {
client.TsigProvider = md5Provider(c.updateKey.secret)
}
}
msg, _, err := client.Exchange(update, c.master)
if err != nil {
return err
}
if msg.MsgHdr.Rcode != 0 {
return fmt.Errorf("[Error] AXFRDDNS: nameserver refused to update the zone: %s (%d)",
dns.RcodeToString[msg.MsgHdr.Rcode],
msg.MsgHdr.Rcode)
}
return nil
},
}
}
// hasDeletionForName returns true if there exist a corrections for [name] which is a deletion
func hasDeletionForName(changes diff2.ChangeList, name string) bool {
for _, change := range changes {
switch change.Type {
case diff2.DELETE:
if change.Old[0].Name == name {
return true
}
}
}
return false
}
// hasNSDeletion returns true if there exist a correction that deletes or changes an NS record
func hasNSDeletion(changes diff2.ChangeList) bool {
for _, change := range changes {
switch change.Type {
case diff2.CHANGE:
if change.Old[0].Type == "NS" && change.Old[0].Name == "@" {
return true
}
case diff2.DELETE:
if change.Old[0].Type == "NS" && change.Old[0].Name == "@" {
return true
}
case diff2.CREATE:
case diff2.REPORT:
}
}
return false
}
// GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records.
func (c *axfrddnsProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, foundRecords models.Records) ([]*models.Correction, error) {
txtutil.SplitSingleLongTxt(foundRecords) // Autosplit long TXT records
// Ignoring the SOA, others providers don't manage it either.
if len(foundRecords) >= 1 && foundRecords[0].Type == "SOA" {
foundRecords = foundRecords[1:]
}
// TODO(tlim): This check should be done on all providers. Move to the global validation code.
if dc.AutoDNSSEC == "on" && !hasDnssecRecords {
if dc.AutoDNSSEC == "on" && !c.hasDnssecRecords {
printer.Printf("Warning: AUTODNSSEC is enabled, but no DNSKEY or RRSIG record was found in the AXFR answer!\n")
}
if dc.AutoDNSSEC == "off" && hasDnssecRecords {
if dc.AutoDNSSEC == "off" && c.hasDnssecRecords {
printer.Printf("Warning: AUTODNSSEC is disabled, but DNSKEY or RRSIG records were found in the AXFR answer!\n")
}
// Normalize
models.PostProcessRecords(foundRecords)
txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records
// An RFC2136-compliant server must silently ignore an
// update that inserts a non-CNAME RRset when a CNAME RR
// with the same name is present in the zone (and
// vice-versa). Therefore we prefer to first remove records
// and then insert new ones.
//
// Compliant servers must also silently ignore an update
// that removes the last NS record of a zone. Therefore we
// don't want to remove all NS records before inserting a
// new one. Then, when an update want to change a NS record,
// we first insert a dummy NS record that we will remove
// at the end of the batched update.
var corrections []*models.Correction
var create, del, mod diff.Changeset
var differ diff.Differ
if !diff2.EnableDiff2 {
differ = diff.New(dc)
} else {
differ = diff.NewCompat(dc)
var msgs []string
var msgs2 []string
update := new(dns.Msg)
update.SetUpdate(dc.Name + ".")
update.Id = uint16(c.rand.Intn(math.MaxUint16))
update2 := new(dns.Msg)
update2.SetUpdate(dc.Name + ".")
update2.Id = uint16(c.rand.Intn(math.MaxUint16))
hasTwoCorrections := false
dummyNs1, err := dns.NewRR(dc.Name + ". IN NS 255.255.255.255")
if err != nil {
return nil, err
}
_, create, del, mod, err = differ.IncrementalDiff(foundRecords)
dummyNs2, err := dns.NewRR(dc.Name + ". IN NS 255.255.255.255")
if err != nil {
return nil, err
}
buf := &bytes.Buffer{}
// Print a list of changes. Generate an actual change that is the zone
changes := false
for _, i := range create {
changes = true
fmt.Fprintln(buf, i)
}
for _, i := range del {
changes = true
fmt.Fprintln(buf, i)
}
for _, i := range mod {
changes = true
fmt.Fprintln(buf, i)
}
msg := fmt.Sprintf("DDNS UPDATES to '%s' (primary master: '%s'). Changes:\n%s", dc.Name, c.master, buf)
if !diff2.EnableDiff2 {
if changes {
// Legacy code with the old `diff`
corrections = append(corrections,
&models.Correction{
Msg: msg,
F: func() error {
differ := diff.New(dc)
_, create, del, mod, err := differ.IncrementalDiff(foundRecords)
if err != nil {
return nil, err
}
// An RFC2136-compliant server must silently ignore an
// update that inserts a non-CNAME RRset when a CNAME RR
// with the same name is present in the zone (and
// vice-versa). Therefore we prefer to first remove records
// and then insert new ones.
//
// Compliant servers must also silently ignore an update
// that removes the last NS record of a zone. Therefore we
// don't want to remove all NS records before inserting a
// new one. For the particular case of NS record, we prefer
// to insert new records before ot remove old ones.
//
// This remarks does not apply for "modified" NS records, as
// updates are processed one-by-one.
//
// This provider does not allow modifying the TTL of an NS
// record in a zone that defines only one NS. That would
// would require removing the single NS record, before
// adding the new one. But who does that anyway?
changes := false
buf := &bytes.Buffer{}
buf2 := &bytes.Buffer{}
update := new(dns.Msg)
update.SetUpdate(dc.Name + ".")
update.Id = uint16(c.rand.Intn(math.MaxUint16))
for _, c := range create {
if c.Desired.Type == "NS" {
update.Insert([]dns.RR{c.Desired.ToRR()})
}
}
for _, c := range del {
update.Remove([]dns.RR{c.Existing.ToRR()})
}
for _, c := range mod {
update.Remove([]dns.RR{c.Existing.ToRR()})
update.Insert([]dns.RR{c.Desired.ToRR()})
}
for _, c := range create {
if c.Desired.Type != "NS" {
update.Insert([]dns.RR{c.Desired.ToRR()})
}
}
// 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
}
}
client := new(dns.Client)
client.Net = c.updateMode
client.Timeout = dnsTimeout
if c.updateKey != nil {
client.TsigSecret =
map[string]string{c.updateKey.id: c.updateKey.secret}
update.SetTsig(c.updateKey.id, c.updateKey.algo, 300, time.Now().Unix())
if c.updateKey.algo == dns.HmacMD5 {
client.TsigProvider = md5Provider(c.updateKey.secret)
}
}
if hasNSDeletion {
update.Insert([]dns.RR{dummyNs1})
}
msg, _, err := client.Exchange(update, c.master)
if err != nil {
return err
}
if msg.MsgHdr.Rcode != 0 {
return fmt.Errorf("[Error] AXFRDDNS: nameserver refused to update the zone: %s (%d)",
dns.RcodeToString[msg.MsgHdr.Rcode],
msg.MsgHdr.Rcode)
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
return nil
},
})
}
return corrections, nil
changes, err := diff2.ByRecord(foundRecords, dc, nil)
if err != nil {
return nil, err
}
if changes == nil {
return nil, nil
}
// A DNS server should silently ignore a DDNS update that removes
// the last NS record of a zone. Since modifying a record is
// implemented by successively a deletion of the old record and an
// insertion of the new one, then modifying all the NS record of a
// zone might will fail (even if the the deletion and insertion
// are grouped in a single batched update).
//
// To avoid this case, we will first insert a dummy NS record,
// that will be removed at the end of the batched updates. This
// record needs to inserted only when all NS records are touched
// The current implementation insert this dummy record as soon as
// a NS record is deleted or changed.
hasNSDeletion := hasNSDeletion(changes)
if hasNSDeletion {
update.Insert([]dns.RR{dummyNs1})
}
for _, change := range changes {
switch change.Type {
case diff2.DELETE:
msgs = append(msgs, change.Msgs[0])
update.Remove([]dns.RR{change.Old[0].ToRR()})
case diff2.CREATE:
if c.serverHasBuggyCNAME &&
change.New[0].Type == "CNAME" &&
hasDeletionForName(changes, change.New[0].Name) {
hasTwoCorrections = true
msgs2 = append(msgs2, change.Msgs[0])
update2.Insert([]dns.RR{change.New[0].ToRR()})
} else {
msgs = append(msgs, change.Msgs[0])
update.Insert([]dns.RR{change.New[0].ToRR()})
}
case diff2.CHANGE:
if c.serverHasBuggyCNAME && change.New[0].Type == "CNAME" {
msgs = append(msgs, change.Msgs[0]+color.RedString(" (delete)"))
update.Remove([]dns.RR{change.Old[0].ToRR()})
hasTwoCorrections = true
msgs2 = append(msgs2, change.Msgs[0]+color.GreenString(" (create)"))
update2.Insert([]dns.RR{change.New[0].ToRR()})
} else {
msgs = append(msgs, change.Msgs[0])
update.Remove([]dns.RR{change.Old[0].ToRR()})
update.Insert([]dns.RR{change.New[0].ToRR()})
}
case diff2.REPORT:
msgs = append(msgs, change.Msgs...)
}
}
if hasNSDeletion {
update.Remove([]dns.RR{dummyNs2})
}
if hasTwoCorrections {
return []*models.Correction{
c.BuildCorrection(dc, msgs, update),
c.BuildCorrection(dc, msgs2, update2),
}, nil
}
return []*models.Correction{
c.BuildCorrection(dc, msgs, update),
}, nil
}

View File

@@ -25,6 +25,8 @@ type azurednsProvider struct {
zones map[string]*adns.Zone
resourceGroup *string
subscriptionID *string
rawRecords map[string][]*adns.RecordSet
zoneName map[string]string
}
func newAzureDNSDsp(conf map[string]string, metadata json.RawMessage) (providers.DNSServiceProvider, error) {
@@ -47,7 +49,14 @@ func newAzureDNS(m map[string]string, metadata json.RawMessage) (*azurednsProvid
return nil, recordErr
}
api := &azurednsProvider{zonesClient: zonesClient, recordsClient: recordsClient, resourceGroup: to.StringPtr(rg), subscriptionID: to.StringPtr(subID)}
api := &azurednsProvider{
zonesClient: zonesClient,
recordsClient: recordsClient,
resourceGroup: to.StringPtr(rg),
subscriptionID: to.StringPtr(subID),
rawRecords: map[string][]*adns.RecordSet{},
zoneName: map[string]string{},
}
err := api.getZones()
if err != nil {
return nil, err
@@ -157,6 +166,7 @@ func (a *azurednsProvider) GetZoneRecords(domain string) (models.Records, error)
if err != nil {
return nil, err
}
return existingRecords, nil
}
@@ -166,40 +176,32 @@ func (a *azurednsProvider) getExistingRecords(domain string) (models.Records, []
return nil, nil, "", errNoExist{domain}
}
zoneName := *zone.Name
records, err := a.fetchRecordSets(zoneName)
rawRecords, err := a.fetchRecordSets(zoneName)
if err != nil {
return nil, nil, "", err
}
var existingRecords models.Records
for _, set := range records {
for _, set := range rawRecords {
existingRecords = append(existingRecords, nativeToRecords(set, zoneName)...)
}
// FIXME(tlim): PostProcessRecords is usually called in GetDomainCorrections.
models.PostProcessRecords(existingRecords)
a.rawRecords[domain] = rawRecords
a.zoneName[domain] = zoneName
// FIXME(tlim): The "records" return value is usually stored in RecordConfig.Original.
return existingRecords, records, zoneName, nil
return existingRecords, rawRecords, zoneName, nil
}
func (a *azurednsProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
err := dc.Punycode()
if err != nil {
return nil, err
}
existingRecords, records, zoneName, err := a.getExistingRecords(dc.Name)
if err != nil {
return nil, err
}
txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records
// GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records.
func (a *azurednsProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, error) {
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 {
@@ -309,13 +311,6 @@ func (a *azurednsProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*mod
F: func() error {
ctx, cancel := context.WithTimeout(context.Background(), 6000*time.Second)
defer cancel()
//fmt.Fprintf(os.Stderr, "DEBUG: 3 a.recordsClient.CreateOrUpdate(ctx, %v, %v, %v, %v, %+v, nil)\n", *a.resourceGroup, zoneName, recordName, recordType, *rrset)
// fmt.Fprintf(os.Stderr, "DEBUG: 3 rrset: %+v\n", *rrset)
// fmt.Fprintf(os.Stderr, "DEBUG: 3 properties: %+v\n", rrset.Properties)
// fmt.Fprintf(os.Stderr, "DEBUG: 3 TargetResource: %+v\n", rrset.Properties.TargetResource)
// if rrset.Properties.TargetResource != nil {
// fmt.Fprintf(os.Stderr, "DEBUG: 3 TargetResourceID: %+v\n", *rrset.Properties.TargetResource.ID)
// }
_, err := a.recordsClient.CreateOrUpdate(ctx, *a.resourceGroup, zoneName, recordName, recordType, *rrset, nil)
if err != nil {
return err
@@ -347,14 +342,8 @@ func (a *azurednsProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*mod
return nil, err
}
// for i, j := range changes {
// fmt.Fprintf(os.Stderr, "DEBUG: CHANGE[%v] = %+v\n", i, j)
// }
for _, change := range changes {
//fmt.Fprintf(os.Stderr, "\n\nCHANGE=%v\n", change)
// Copy all param values to local variables to avoid overwrites
msgs := change.MsgsJoined
dcn := dc.Name
@@ -364,34 +353,14 @@ func (a *azurednsProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*mod
case diff2.REPORT:
corrections = append(corrections, &models.Correction{Msg: change.MsgsJoined})
case diff2.CHANGE, diff2.CREATE:
changeNew := change.New
// for i, j := range change.Old {
// fmt.Fprintf(os.Stderr, "DEBUG: OLD[%d] = %+v ttl=%d\n", i, j, j.TTL)
// }
// for i, j := range change.New {
// fmt.Fprintf(os.Stderr, "DEBUG: NEW[%d] = %+v ttl=%d\n", i, j, j.TTL)
// }
// fmt.Fprintf(os.Stderr, "DEBUG: CHANGE = \n%v\n", change)
corrections = append(corrections, &models.Correction{
Msg: msgs,
F: func() error {
return a.recordCreate(dcn, chaKey, changeNew)
},
})
case diff2.DELETE:
// for i, j := range change.Old {
// fmt.Fprintf(os.Stderr, "DEBUG: OLD[%d] = %+v\n", i, j)
// }
// for i, j := range change.New {
// fmt.Fprintf(os.Stderr, "DEBUG: NEW[%d] = %+v\n", i, j)
// }
//fmt.Fprintf(os.Stderr, "DEBUG: CHANGE = \n%v\n", change)
corrections = append(corrections, &models.Correction{
Msg: msgs,
F: func() error {
@@ -414,31 +383,16 @@ func (a *azurednsProvider) recordCreate(zoneName string, reckey models.RecordKey
return err
}
// rrset, _, err := a.recordToNativeDiff2(reckey, recs)
// if err != nil {
// return err
// }
var recordName string
var i int64
for _, r := range recs {
i = int64(r.TTL)
recordName = r.Name
//fmt.Fprintf(os.Stderr, "DEBUG: rn=%v ttl=%d\n", recordName, i)
}
rrset.Properties.TTL = &i
ctx, cancel := context.WithTimeout(context.Background(), 6000*time.Second)
defer cancel()
//fmt.Fprintf(os.Stderr, "DEBUG: a.recordsClient.CreateOrUpdate(%v, %v, %v, %v, %+v)\n", *a.resourceGroup, zoneName, recordName, azRecType, *rrset)
//fmt.Fprintf(os.Stderr, "DEBUG: rrset.Properties=%+v\n", *rrset.Properties)
//fmt.Fprintf(os.Stderr, "DEBUG: recordCreate a.recordsClient.CreateOrUpdate(ctx, %v, %v, %v, %v, %+v, nil)\n", *a.resourceGroup, zoneName, recordName, azRecType, *rrset)
//fmt.Fprintf(os.Stderr, "DEBUG: recordCreate rrset: %+v\n", *rrset)
//fmt.Fprintf(os.Stderr, "DEBUG: recordCreate properties: %+v\n", rrset.Properties)
//fmt.Fprintf(os.Stderr, "DEBUG: recordCreate TargetResource: %+v\n", rrset.Properties.TargetResource)
// if rrset.Properties.TargetResource != nil {
// fmt.Fprintf(os.Stderr, "DEBUG: recordCreate TargetResourceID: %+v\n", *rrset.Properties.TargetResource.ID)
// }
_, err = a.recordsClient.CreateOrUpdate(ctx, *a.resourceGroup, zoneName, recordName, azRecType, *rrset, nil)
return err
}
@@ -446,12 +400,10 @@ func (a *azurednsProvider) recordCreate(zoneName string, reckey models.RecordKey
func (a *azurednsProvider) recordDelete(zoneName string, reckey models.RecordKey, recs models.Records) error {
shortName := strings.TrimSuffix(reckey.NameFQDN, "."+zoneName)
//_, azRecType, err := a.recordToNative(reckey, recs)
if shortName == zoneName {
shortName = "@"
}
//azRecType, err := nativeToRecordType(to.StringPtr(*recordSet.Type))
azRecType, err := nativeToRecordTypeDiff2(to.StringPtr(reckey.Type))
if err != nil {
return nil
@@ -459,7 +411,6 @@ func (a *azurednsProvider) recordDelete(zoneName string, reckey models.RecordKey
ctx, cancel := context.WithTimeout(context.Background(), 6000*time.Second)
defer cancel()
//fmt.Fprintf(os.Stderr, "DEBUG: a.recordsClient.Delete(%v, %v, %v, %v)\n", *a.resourceGroup, zoneName, shortName, azRecType)
_, err = a.recordsClient.Delete(ctx, *a.resourceGroup, zoneName, shortName, azRecType, nil)
return err
}
@@ -524,7 +475,6 @@ func nativeToRecordTypeDiff2(recordType *string) (adns.RecordType, error) {
func safeTarget(t *string) string {
if t == nil {
//panic("no TARGET")
return "foundnil"
}
return *t
@@ -802,34 +752,11 @@ func (a *azurednsProvider) recordToNativeDiff2(recordKey models.RecordKey, recor
}
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":
//case "AZURE_ALIAS":
// fmt.Fprintf(os.Stderr, "DEBUG: AZURE_ALIAS recordToNativeDiff2 rec=%+v\n", rec)
// fmt.Fprintf(os.Stderr, "DEBUG: AZURE_ALIAS rec.Type=%v\n", rec.Type)
// fmt.Fprintf(os.Stderr, "DEBUG: AZURE_ALIAS rec.TTL=%v\n", rec.TTL)
// fmt.Fprintf(os.Stderr, "DEBUG: AZURE_ALIAS rec.Label=%v\n", rec.GetLabel())
// fmt.Fprintf(os.Stderr, "DEBUG: AZURE_ALIAS rec.Target=%v\n", rec.GetTargetField())
// fmt.Fprintf(os.Stderr, "DEBUG: AZURE_ALIAS rec.AzureAlias=%v\n", rec.AzureAlias)
// OLD *recordSet.Type = rec.AzureAlias["type"]
// NEW:
aatype := rec.AzureAlias["type"]
recordSet.Type = &aatype
// if aatype == "CNAME" {
// fmt.Fprintf(os.Stderr, "DEBUG: AZURE_ALIAS Cname -> MS.n.dnszones.CNAME\n")
// aatype = "Microsoft.Network/dnszones/CNAME"
// recordSet.Properties.CnameRecord = adns.CnameRecord{Cname: to.StringPtr(rec.GetTargetField())}
// // recordSet.Properties.CnameRecord.Cname
// //recordSet.Properties.CnameRecord = &adns.CnameRecord{Cname: to.StringPtr(rec.GetTargetField())}
// }
// OLD: recordSet.Properties.TargetResource = &adns.SubResource{ID: to.StringPtr(rec.GetTargetField())}
// NEW:
aatarg := to.StringPtr(rec.GetTargetField())
aasub := adns.SubResource{ID: aatarg}
recordSet.Properties.TargetResource = &aasub
// fmt.Fprintf(os.Stderr, "DEBUG: AZURE_ALIAS rrset.Type=%v\n", *recordSet.Type)
// fmt.Fprintf(os.Stderr, "DEBUG: AZURE_ALIAS rrset.Target=%v\n", *aatarg)
// fmt.Fprintf(os.Stderr, "DEBUG: AZURE_ALIAS rrset.TargetRes=%+v\n", aasub)
default:
return nil, adns.RecordTypeA, fmt.Errorf("recordToNativeDiff2 RTYPE %v UNIMPLEMENTED", recordKeyType) // ands.A is a placeholder

View File

@@ -27,7 +27,6 @@ import (
"github.com/StackExchange/dnscontrol/v3/pkg/diff2"
"github.com/StackExchange/dnscontrol/v3/pkg/prettyzone"
"github.com/StackExchange/dnscontrol/v3/pkg/printer"
"github.com/StackExchange/dnscontrol/v3/pkg/txtutil"
"github.com/StackExchange/dnscontrol/v3/providers"
"github.com/miekg/dns"
)
@@ -202,29 +201,11 @@ func ParseZoneContents(content string, zoneName string, zonefileName string) (mo
return foundRecords, nil
}
// GetDomainCorrections returns a list of corrections to update a domain.
func (c *bindProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
dc.Punycode()
// 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) {
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.UniqueName, dc.Name, dc.Tag))
foundRecords, err := c.GetZoneRecords(dc.Name)
if err != nil {
return nil, err
}
changes := false
var msg string
// Find the SOA records; use them to make or update the desired SOA.
var foundSoa *models.RecordConfig
@@ -250,13 +231,6 @@ func (c *bindProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.
c.skipNextSoaIncrease = true
}
// Normalize
models.PostProcessRecords(foundRecords)
txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records
changes := false
var msg string
if !diff2.EnableDiff2 {
differ := diff.New(dc)
@@ -296,6 +270,7 @@ func (c *bindProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.
} else {
var msgs []string
var err error
msgs, changes, err = diff2.ByZone(foundRecords, dc, nil)
if err != nil {
return nil, err
@@ -309,6 +284,18 @@ func (c *bindProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.
//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")
}
// We only change the serial number if there is a change.
if !c.skipNextSoaIncrease {
desiredSoa.SoaSerial = nextSerial

View File

@@ -3,7 +3,6 @@ package cloudflare
import (
"encoding/json"
"fmt"
"log"
"net"
"strings"
@@ -15,7 +14,6 @@ import (
"github.com/StackExchange/dnscontrol/v3/providers"
"github.com/cloudflare/cloudflare-go"
"github.com/fatih/color"
"github.com/miekg/dns/dnsutil"
)
/*
@@ -112,17 +110,19 @@ func (c *cloudflareProvider) ListZones() ([]string, error) {
// GetZoneRecords gets the records of a zone and returns them in RecordConfig format.
func (c *cloudflareProvider) GetZoneRecords(domain string) (models.Records, error) {
id, err := c.getDomainID(domain)
domainID, err := c.getDomainID(domain)
if err != nil {
return nil, err
}
records, err := c.getRecordsForDomain(id, domain)
records, err := c.getRecordsForDomain(domainID, domain)
if err != nil {
return nil, err
}
for _, rec := range records {
if rec.TTL == 1 {
rec.TTL = 0
if rec.TTL == 0 {
rec.TTL = 1
}
// Store the proxy status ("orange cloud") for use by get-zones:
m := getProxyMetadata(rec)
@@ -133,6 +133,37 @@ func (c *cloudflareProvider) GetZoneRecords(domain string) (models.Records, erro
rec.Metadata["cloudflare_proxy"] = p
}
}
// // FIXME(tlim) Why is this needed???
// // I don't know. Let's comment it out and see if anything breaks.
// for i := len(records) - 1; i >= 0; i-- {
// rec := records[i]
// // Delete ignore labels
// if labelMatches(dnsutil.TrimDomainName(rec.Original.(cloudflare.DNSRecord).Name, dc.Name), c.ignoredLabels) {
// printer.Debugf("ignored_label: %s\n", rec.Original.(cloudflare.DNSRecord).Name)
// records = append(records[:i], records[i+1:]...)
// }
// }
if c.manageRedirects {
prs, err := c.getPageRules(domainID, domain)
if err != nil {
return nil, err
}
records = append(records, prs...)
}
if c.manageWorkers {
wrs, err := c.getWorkerRoutes(domainID, domain)
if err != nil {
return nil, err
}
records = append(records, wrs...)
}
// Normalize
models.PostProcessRecords(records)
return records, nil
}
@@ -149,52 +180,26 @@ func (c *cloudflareProvider) getDomainID(name string) (string, error) {
return id, nil
}
// GetDomainCorrections returns a list of corrections to update a domain.
func (c *cloudflareProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
err := dc.Punycode()
if err != nil {
return nil, err
}
domainID, err := c.getDomainID(dc.Name)
if err != nil {
return nil, err
}
records, err := c.getRecordsForDomain(domainID, dc.Name)
if err != nil {
return nil, err
}
// GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records.
func (c *cloudflareProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, records models.Records) ([]*models.Correction, error) {
if err := c.preprocessConfig(dc); err != nil {
return nil, err
}
for i := len(records) - 1; i >= 0; i-- {
rec := records[i]
// Delete ignore labels
if labelMatches(dnsutil.TrimDomainName(rec.Original.(cloudflare.DNSRecord).Name, dc.Name), c.ignoredLabels) {
printer.Debugf("ignored_label: %s\n", rec.Original.(cloudflare.DNSRecord).Name)
records = append(records[:i], records[i+1:]...)
}
}
// for i := len(records) - 1; i >= 0; i-- {
// rec := records[i]
// // Delete ignore labels
// if labelMatches(dnsutil.TrimDomainName(rec.Original.(cloudflare.DNSRecord).Name, dc.Name), c.ignoredLabels) {
// printer.Debugf("ignored_label: %s\n", rec.Original.(cloudflare.DNSRecord).Name)
// records = append(records[:i], records[i+1:]...)
// }
// }
if c.manageRedirects {
prs, err := c.getPageRules(domainID, dc.Name)
//printer.Printf("GET PAGE RULES:\n")
//for i, p := range prs {
// printer.Printf("%03d: %q\n", i, p.GetTargetField())
//}
if err != nil {
return nil, err
}
records = append(records, prs...)
}
checkNSModifications(dc)
if c.manageWorkers {
wrs, err := c.getWorkerRoutes(domainID, dc.Name)
if err != nil {
return nil, err
}
records = append(records, wrs...)
domainID, err := c.getDomainID(dc.Name)
if err != nil {
return nil, err
}
for _, rec := range dc.Records {
@@ -207,24 +212,13 @@ func (c *cloudflareProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*m
if rec.Metadata[metaProxy] != "off" {
rec.TTL = 1
}
if labelMatches(rec.GetLabel(), c.ignoredLabels) {
log.Fatalf("FATAL: dnsconfig contains label that matches ignored_labels: %#v is in %v)\n", rec.GetLabel(), c.ignoredLabels)
}
// if labelMatches(rec.GetLabel(), c.ignoredLabels) {
// log.Fatalf("FATAL: dnsconfig contains label that matches ignored_labels: %#v is in %v)\n", rec.GetLabel(), c.ignoredLabels)
// }
}
checkNSModifications(dc)
// Normalize
models.PostProcessRecords(records)
//txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records
// Don't split.
// Cloudflare's API only supports one TXT string of any non-zero length. No
// multiple strings.
// When serving the DNS record, it splits strings >255 octets into
// individual segments of 255 each. However that is hidden from the API.
// Therefore, whether the string is 1 octet or thousands, just store it as
// one string in the first element of .TxtStrings.
var corrections []*models.Correction
if !diff2.EnableDiff2 {
@@ -347,7 +341,7 @@ func (c *cloudflareProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*m
corrs = c.mkCreateCorrection(createRec, domainID, msg)
// DS records must always have a corresponding NS record.
// Therefore, we create NS records before any DS records.
addToFront = createRec.Type == "NS"
addToFront = (createRec.Type == "NS")
case diff2.CHANGE:
newrec := inst.New[0]
oldrec := inst.Old[0]
@@ -359,7 +353,7 @@ func (c *cloudflareProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*m
corrs = c.mkDeleteCorrection(deleteRecType, deleteRecOrig, domainID, msg)
// DS records must always have a corresponding NS record.
// Therefore, we remove DS records before any NS records.
addToFront = deleteRecType == "DS"
addToFront = (deleteRecType == "DS")
}
if addToFront {
@@ -387,13 +381,16 @@ func (c *cloudflareProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*m
}
func genComparable(rec *models.RecordConfig) string {
//fmt.Printf("DEBUG: genComparable called %v:%v meta=%+v\n", rec.Type, rec.GetLabel(), rec.Metadata)
if rec.Type == "A" || rec.Type == "AAAA" || rec.Type == "CNAME" {
proxy := rec.Metadata[metaProxy]
if proxy != "" {
//return "proxy=" + rec.Metadata[metaProxy]
if proxy == "on" {
proxy = "true"
}
if proxy == "off" {
proxy = "false"
}
return "proxy=" + proxy
//return fmt.Sprintf("proxy:%v=%s", rec.Type, proxy)
}
}
return ""
@@ -417,6 +414,7 @@ func (c *cloudflareProvider) mkCreateCorrection(newrec *models.RecordConfig, dom
}
func (c *cloudflareProvider) mkChangeCorrection(oldrec, newrec *models.RecordConfig, domainID string, msg string) []*models.Correction {
var idTxt string
switch oldrec.Type {
case "PAGE_RULE":
@@ -433,14 +431,14 @@ func (c *cloudflareProvider) mkChangeCorrection(oldrec, newrec *models.RecordCon
return []*models.Correction{{
Msg: msg,
F: func() error {
return c.updatePageRule(oldrec.Original.(cloudflare.PageRule).ID, domainID, newrec.GetTargetField())
return c.updatePageRule(idTxt, domainID, newrec.GetTargetField())
},
}}
case "WORKER_ROUTE":
return []*models.Correction{{
Msg: msg,
F: func() error {
return c.updateWorkerRoute(oldrec.Original.(cloudflare.WorkerRoute).ID, domainID, newrec.GetTargetField())
return c.updateWorkerRoute(idTxt, domainID, newrec.GetTargetField())
},
}}
default:

View File

@@ -109,6 +109,9 @@ func (c *cloudnsProvider) fetchAvailableTTLValues(domain string) error {
}
func (c *cloudnsProvider) fetchDomainList() error {
// FIXME(tlim): This should return nil if c.domainIndex != nil. Then all
// the callers won't have to check if c.domainIndex == nil before calling.
c.domainIndex = map[string]string{}
rowsPerPage := 100
page := 1

View File

@@ -71,14 +71,45 @@ func (c *cloudnsProvider) GetNameservers(domain string) ([]*models.Nameserver, e
return models.ToNameservers(c.nameserversNames)
}
// GetDomainCorrections returns the corrections for a domain.
func (c *cloudnsProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
dc, err := dc.Copy()
if err != nil {
return nil, err
}
// // GetDomainCorrections returns the corrections for a domain.
// func (c *cloudnsProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
// dc, err := dc.Copy()
// if err != nil {
// return nil, err
// }
dc.Punycode()
// dc.Punycode()
// if c.domainIndex == nil {
// if err := c.fetchDomainList(); err != nil {
// return nil, err
// }
// }
// _, ok := c.domainIndex[dc.Name]
// if !ok {
// return nil, fmt.Errorf("'%s' not a zone in ClouDNS account", dc.Name)
// }
// existingRecords, err := c.GetZoneRecords(dc.Name)
// if err != nil {
// return nil, err
// }
// // Normalize
// models.PostProcessRecords(existingRecords)
// // Get a list of available TTL values.
// // The TTL list needs to be obtained for each domain, so get it first here.
// c.fetchAvailableTTLValues(dc.Name)
// // ClouDNS can only be specified from a specific TTL list, so change the TTL in advance.
// for _, record := range dc.Records {
// record.TTL = fixTTL(record.TTL)
// }
// return c.GetZoneRecordsCorrections(dc, existingRecords)
// }
// GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records.
func (c *cloudnsProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, error) {
if c.domainIndex == nil {
if err := c.fetchDomainList(); err != nil {
@@ -90,13 +121,6 @@ func (c *cloudnsProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*mode
return nil, fmt.Errorf("'%s' not a zone in ClouDNS account", dc.Name)
}
existingRecords, err := c.GetZoneRecords(dc.Name)
if err != nil {
return nil, err
}
// Normalize
models.PostProcessRecords(existingRecords)
// Get a list of available TTL values.
// The TTL list needs to be obtained for each domain, so get it first here.
c.fetchAvailableTTLValues(dc.Name)

View File

@@ -75,46 +75,8 @@ func (client *providerClient) GetNameservers(domain string) ([]*models.Nameserve
return models.ToNameservers(nss)
}
// GetDomainCorrections get the current and existing records,
// post-process them, and generate corrections.
// NB(tlim): This function should be exactly the same in all DNS providers. Once
// all providers do this, we can eliminate it and use a Go interface instead.
func (client *providerClient) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
existing, err := client.GetZoneRecords(dc.Name)
if err != nil {
return nil, err
}
models.PostProcessRecords(existing)
//txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records
clean := PrepFoundRecords(existing)
PrepDesiredRecords(dc)
return client.GenerateDomainCorrections(dc, clean)
}
// PrepFoundRecords munges any records to make them compatible with
// this provider. Usually this is a no-op.
func PrepFoundRecords(recs models.Records) models.Records {
// If there are records that need to be modified, removed, etc. we
// do it here. Usually this is a no-op.
return recs
}
// PrepDesiredRecords munges any records to best suit this provider.
func PrepDesiredRecords(dc *models.DomainConfig) {
// Sort through the dc.Records, eliminate any that can't be
// supported; modify any that need adjustments to work with the
// provider. We try to do minimal changes otherwise it gets
// confusing.
dc.Punycode()
}
// GetDomainCorrections gets existing records, diffs them against existing, and returns corrections.
func (client *providerClient) GenerateDomainCorrections(dc *models.DomainConfig, foundRecords models.Records) ([]*models.Correction, error) {
// Normalize
models.PostProcessRecords(foundRecords)
// GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records.
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

View File

@@ -74,28 +74,28 @@ func (c *desecProvider) GetNameservers(domain string) ([]*models.Nameserver, err
return models.ToNameservers(defaultNameServerNames)
}
func (c *desecProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
if dc.AutoDNSSEC == "off" {
printer.Printf("Notice: DNSSEC signing was not requested, but cannot be turned off. (deSEC always signs all records.)\n")
}
// func (c *desecProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
// if dc.AutoDNSSEC == "off" {
// printer.Printf("Notice: DNSSEC signing was not requested, but cannot be turned off. (deSEC always signs all records.)\n")
// }
existing, err := c.GetZoneRecords(dc.Name)
if err != nil {
return nil, err
}
models.PostProcessRecords(existing)
clean := PrepFoundRecords(existing)
var minTTL uint32
c.mutex.Lock()
if ttl, ok := c.domainIndex[dc.Name]; !ok {
minTTL = 3600
} else {
minTTL = ttl
}
c.mutex.Unlock()
PrepDesiredRecords(dc, minTTL)
return c.GenerateDomainCorrections(dc, clean)
}
// existing, err := c.GetZoneRecords(dc.Name)
// if err != nil {
// return nil, err
// }
// models.PostProcessRecords(existing)
// clean := PrepFoundRecords(existing)
// var minTTL uint32
// c.mutex.Lock()
// if ttl, ok := c.domainIndex[dc.Name]; !ok {
// minTTL = 3600
// } else {
// minTTL = ttl
// }
// c.mutex.Unlock()
// PrepDesiredRecords(dc, minTTL)
// return c.GetZoneRecordsCorrections(dc, clean)
// }
// GetZoneRecords gets the records of a zone and returns them in RecordConfig format.
func (c *desecProvider) GetZoneRecords(domain string) (models.Records, error) {
@@ -110,6 +110,7 @@ func (c *desecProvider) GetZoneRecords(domain string) (models.Records, error) {
for _, rr := range records {
existingRecords = append(existingRecords, nativeToRecords(rr, domain)...)
}
return existingRecords, nil
}
@@ -123,14 +124,6 @@ func (c *desecProvider) EnsureZoneExists(domain string) error {
return c.createDomain(domain)
}
// PrepFoundRecords munges any records to make them compatible with
// this provider. Usually this is a no-op.
func PrepFoundRecords(recs models.Records) models.Records {
// If there are records that need to be modified, removed, etc. we
// do it here. Usually this is a no-op.
return recs
}
// PrepDesiredRecords munges any records to best suit this provider.
func PrepDesiredRecords(dc *models.DomainConfig, minTTL uint32) {
// Sort through the dc.Records, eliminate any that can't be
@@ -138,8 +131,7 @@ func PrepDesiredRecords(dc *models.DomainConfig, minTTL uint32) {
// provider. We try to do minimal changes otherwise it gets
// confusing.
dc.Punycode()
txtutil.SplitSingleLongTxt(dc.Records)
//dc.Punycode()
recordsToKeep := make([]*models.RecordConfig, 0, len(dc.Records))
for _, rec := range dc.Records {
if rec.Type == "ALIAS" {
@@ -158,12 +150,19 @@ func PrepDesiredRecords(dc *models.DomainConfig, minTTL uint32) {
dc.Records = recordsToKeep
}
// GenerateDomainCorrections takes the desired and existing records
// and produces a Correction list. The correction list is simply
// a list of functions to call to actually make the desired
// correction, and a message to output to the user when the change is
// made.
func (c *desecProvider) GenerateDomainCorrections(dc *models.DomainConfig, existing models.Records) ([]*models.Correction, error) {
// GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records.
func (c *desecProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existing models.Records) ([]*models.Correction, error) {
txtutil.SplitSingleLongTxt(dc.Records)
var minTTL uint32
c.mutex.Lock()
if ttl, ok := c.domainIndex[dc.Name]; !ok {
minTTL = 3600
} else {
minTTL = ttl
}
c.mutex.Unlock()
PrepDesiredRecords(dc, minTTL)
var corrections []*models.Correction
var err error

View File

@@ -133,20 +133,12 @@ func (api *digitaloceanProvider) GetZoneRecords(domain string) (models.Records,
return existingRecords, nil
}
// GetDomainCorrections returns a list of corretions for the domain.
func (api *digitaloceanProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
ctx := context.Background()
dc.Punycode()
existingRecords, err := api.GetZoneRecords(dc.Name)
if err != nil {
return nil, err
}
// Normalize
models.PostProcessRecords(existingRecords)
// GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records.
func (api *digitaloceanProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, error) {
txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records
ctx := context.Background()
var corrections []*models.Correction
var differ diff.Differ
if !diff2.EnableDiff2 {

View File

@@ -133,17 +133,16 @@ func (c *dnsimpleProvider) GetZoneRecords(domain string) (models.Records, error)
cleanedRecords = append(cleanedRecords, rec)
}
// Apex NS are immutable via API
cleanedRecords = removeApexNS(cleanedRecords)
return cleanedRecords, nil
}
// GetDomainCorrections returns corrections that update a domain.
func (c *dnsimpleProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
func (c *dnsimpleProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, actual models.Records) ([]*models.Correction, error) {
var corrections []*models.Correction
err := dc.Punycode()
if err != nil {
return nil, err
}
removeOtherApexNS(dc)
dnssecFixes, err := c.getDNSSECCorrections(dc)
if err != nil {
@@ -151,25 +150,13 @@ func (c *dnsimpleProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*mod
}
corrections = append(corrections, dnssecFixes...)
records, err := c.GetZoneRecords(dc.Name)
if err != nil {
return nil, err
}
// Apex NS are immutable via API
actual := removeApexNS(records)
removeOtherApexNS(dc)
// Normalize
models.PostProcessRecords(actual)
var create, del, modify diff.Changeset
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 := differ.IncrementalDiff(actual)
if err != nil {
return nil, err
}

View File

@@ -63,14 +63,47 @@ func New(settings map[string]string, _ json.RawMessage) (providers.DNSServicePro
return api, nil
}
// GetDomainCorrections returns the corrections for a domain.
func (api *dnsMadeEasyProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
dc, err := dc.Copy()
if err != nil {
return nil, err
}
// // GetDomainCorrections returns the corrections for a domain.
// func (api *dnsMadeEasyProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
// dc, err := dc.Copy()
// if err != nil {
// return nil, err
// }
err = dc.Punycode()
// err = dc.Punycode()
// if err != nil {
// return nil, err
// }
// for _, rec := range dc.Records {
// if rec.Type == "ALIAS" {
// // ALIAS is called ANAME on DNS Made Easy
// rec.Type = "ANAME"
// } else if rec.Type == "NS" {
// // NS records have fixed TTL on DNS Made Easy and it cannot be changed
// rec.TTL = fixedNameServerRecordTTL
// }
// }
// domainName := dc.Name
// // Get existing records
// existingRecords, err := api.GetZoneRecords(domainName)
// if err != nil {
// return nil, err
// }
// // Normalize
// models.PostProcessRecords(existingRecords)
// return api.GetZoneRecordsCorrections(dc, existingRecords)
// }
func (api *dnsMadeEasyProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, error) {
txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records
domainName := dc.Name
domain, err := api.findDomain(domainName)
if err != nil {
return nil, err
}
@@ -85,32 +118,14 @@ func (api *dnsMadeEasyProvider) GetDomainCorrections(dc *models.DomainConfig) ([
}
}
domainName := dc.Name
domain, err := api.findDomain(domainName)
if err != nil {
return nil, err
}
// Get existing records
existingRecords, err := api.GetZoneRecords(domainName)
if err != nil {
return nil, err
}
// Normalize
models.PostProcessRecords(existingRecords)
txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records
var corrections []*models.Correction
var create, del, modify diff.Changeset
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 := differ.IncrementalDiff(existingRecords)
if err != nil {
return nil, err
}

View File

@@ -25,15 +25,8 @@ func (api *domainNameShopProvider) GetZoneRecords(domain string) (models.Records
return existingRecords, nil
}
func (api *domainNameShopProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
dc.Punycode()
existingRecords, err := api.GetZoneRecords(dc.Name)
if err != nil {
return nil, err
}
// Normalize
models.PostProcessRecords(existingRecords)
// GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records.
func (api *domainNameShopProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, error) {
// Merge TXT strings to one string
for _, rc := range dc.Records {
@@ -48,14 +41,13 @@ func (api *domainNameShopProvider) GetDomainCorrections(dc *models.DomainConfig)
}
var corrections []*models.Correction
var create, delete, modify diff.Changeset
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 := differ.IncrementalDiff(existingRecords)
if err != nil {
return nil, err
}

View File

@@ -88,22 +88,13 @@ func (c *exoscaleProvider) GetNameservers(domain string) ([]*models.Nameserver,
}
// GetZoneRecords gets the records of a zone and returns them in RecordConfig format.
func (c *exoscaleProvider) GetZoneRecords(domain string) (models.Records, error) {
return nil, fmt.Errorf("not implemented")
// This enables the get-zones subcommand.
// Implement this by extracting the code from GetDomainCorrections into
// a single function. For most providers this should be relatively easy.
}
func (c *exoscaleProvider) GetZoneRecords(domainName string) (models.Records, error) {
//dc.Punycode()
// GetDomainCorrections returns a list of corretions for the domain.
func (c *exoscaleProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
dc.Punycode()
domain, err := c.findDomainByName(dc.Name)
domain, err := c.findDomainByName(domainName)
if err != nil {
return nil, err
}
domainID := *domain.ID
ctx := context.Background()
@@ -165,7 +156,7 @@ func (c *exoscaleProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*mod
if record.TTL != nil {
rc.TTL = uint32(*record.TTL)
}
rc.SetLabel(rname, dc.Name)
rc.SetLabel(rname, domainName)
switch rtype {
case "ALIAS", "URL":
@@ -178,7 +169,7 @@ func (c *exoscaleProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*mod
}
err = rc.SetTargetMX(prio, rcontent)
default:
err = rc.PopulateFromString(rtype, rcontent, dc.Name)
err = rc.PopulateFromString(rtype, rcontent, domainName)
}
if err != nil {
return nil, fmt.Errorf("unparsable record received from exoscale: %w", err)
@@ -186,25 +177,33 @@ func (c *exoscaleProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*mod
existingRecords = append(existingRecords, rc)
}
removeOtherNS(dc)
// Normalize
models.PostProcessRecords(existingRecords)
return existingRecords, nil
}
// GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records.
func (c *exoscaleProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, error) {
removeOtherNS(dc)
domain, err := c.findDomainByName(dc.Name)
if err != nil {
return nil, err
}
domainID := *domain.ID
var corrections []*models.Correction
var create, delete, modify diff.Changeset
var differ diff.Differ
if !diff2.EnableDiff2 {
differ = diff.New(dc)
} else {
differ = diff.NewCompat(dc)
}
_, create, delete, modify, err = differ.IncrementalDiff(existingRecords)
_, create, toDelete, modify, err := differ.IncrementalDiff(existingRecords)
if err != nil {
return nil, err
}
for _, del := range delete {
for _, del := range toDelete {
record := del.Existing.Original.(*egoscale.DNSDomainRecord)
corrections = append(corrections, &models.Correction{
Msg: del.String(),

View File

@@ -127,29 +127,6 @@ func newHelper(m map[string]string, metadata json.RawMessage) (*gandiv5Provider,
// return zones, nil
// }
// NB(tal): To future-proof your code, all new providers should
// implement GetDomainCorrections exactly as you see here
// (byte-for-byte the same). In 3.0
// we plan on using just the individual calls to GetZoneRecords,
// PostProcessRecords, and so on.
//
// Currently every provider does things differently, which prevents
// us from doing things like using GetZoneRecords() of a provider
// to make convertzone work with all providers.
// GetDomainCorrections get the current and existing records,
// post-process them, and generate corrections.
func (client *gandiv5Provider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
existing, err := client.GetZoneRecords(dc.Name)
if err != nil {
return nil, err
}
models.PostProcessRecords(existing)
clean := PrepFoundRecords(existing)
PrepDesiredRecords(dc)
return client.GenerateDomainCorrections(dc, clean)
}
// GetZoneRecords gathers the DNS records and converts them to
// dnscontrol's format.
func (client *gandiv5Provider) GetZoneRecords(domain string) (models.Records, error) {
@@ -178,14 +155,6 @@ func (client *gandiv5Provider) GetZoneRecords(domain string) (models.Records, er
return existingRecords, nil
}
// PrepFoundRecords munges any records to make them compatible with
// this provider. Usually this is a no-op.
func PrepFoundRecords(recs models.Records) models.Records {
// If there are records that need to be modified, removed, etc. we
// do it here. Usually this is a no-op.
return recs
}
// PrepDesiredRecords munges any records to best suit this provider.
func PrepDesiredRecords(dc *models.DomainConfig) {
// Sort through the dc.Records, eliminate any that can't be
@@ -193,8 +162,6 @@ func PrepDesiredRecords(dc *models.DomainConfig) {
// provider. We try to do minimal changes otherwise it gets
// confusing.
dc.Punycode()
recordsToKeep := make([]*models.RecordConfig, 0, len(dc.Records))
for _, rec := range dc.Records {
if rec.Type == "ALIAS" && rec.Name != "@" {
@@ -224,16 +191,13 @@ func PrepDesiredRecords(dc *models.DomainConfig) {
dc.Records = recordsToKeep
}
// GenerateDomainCorrections takes the desired and existing records
// and produces a Correction list. The correction list is simply
// a list of functions to call to actually make the desired
// correction, and a message to output to the user when the change is
// made.
func (client *gandiv5Provider) GenerateDomainCorrections(dc *models.DomainConfig, existing models.Records) ([]*models.Correction, error) {
// 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) {
if client.debug {
debugRecords("GenDC input", existing)
}
PrepDesiredRecords(dc)
txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records
var corrections []*models.Correction
@@ -368,7 +332,7 @@ func (client *gandiv5Provider) GenerateDomainCorrections(dc *models.DomainConfig
case diff2.CREATE:
// We have to create the label one rtype at a time.
// In other words, this is a ByRecordSet API for creation, even though
// this is a ByLabel() API for everything else.
// this is very ByLabel()-ish for everything else.
natives := recordsToNative(inst.New, dc.Name)
for _, n := range natives {
label := inst.Key.NameFQDN

View File

@@ -53,6 +53,9 @@ type gcloudProvider struct {
project string
nameServerSet *string
zones map[string]*gdns.ManagedZone
// diff1
oldRRsMap map[string]map[key]*gdns.ResourceRecordSet
zoneNameMap map[string]string
}
type errNoExist struct {
@@ -105,6 +108,8 @@ func New(cfg map[string]string, metadata json.RawMessage) (providers.DNSServiceP
client: dcli,
nameServerSet: nss,
project: cfg["project_id"],
oldRRsMap: map[string]map[key]*gdns.ResourceRecordSet{},
zoneNameMap: map[string]string{},
}
return g, g.loadZoneInfo()
}
@@ -168,14 +173,14 @@ func keyForRec(r *models.RecordConfig) key {
// GetZoneRecords gets the records of a zone and returns them in RecordConfig format.
func (g *gcloudProvider) GetZoneRecords(domain string) (models.Records, error) {
existingRecords, _, _, err := g.getZoneSets(domain)
existingRecords, err := g.getZoneSets(domain)
return existingRecords, err
}
func (g *gcloudProvider) getZoneSets(domain string) (models.Records, map[key]*gdns.ResourceRecordSet, string, error) {
func (g *gcloudProvider) getZoneSets(domain string) (models.Records, error) {
rrs, zoneName, err := g.getRecords(domain)
if err != nil {
return nil, nil, "", err
return nil, err
}
// convert to dnscontrol RecordConfig format
existingRecords := []*models.RecordConfig{}
@@ -185,28 +190,33 @@ func (g *gcloudProvider) getZoneSets(domain string) (models.Records, map[key]*gd
for _, rec := range set.Rrdatas {
rt, err := nativeToRecord(set, rec, domain)
if err != nil {
return nil, nil, "", err
return nil, err
}
existingRecords = append(existingRecords, rt)
}
}
return existingRecords, oldRRs, zoneName, err
g.oldRRsMap[domain] = oldRRs
g.zoneNameMap[domain] = zoneName
return existingRecords, err
}
func (g *gcloudProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
if err := dc.Punycode(); err != nil {
return nil, fmt.Errorf("punycode error: %w", err)
}
existingRecords, oldRRs, zoneName, err := g.getZoneSets(dc.Name)
if err != nil {
return nil, fmt.Errorf("getzonesets error: %w", err)
}
// 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) {
// Normalize
models.PostProcessRecords(existingRecords)
txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records
oldRRs, ok := g.oldRRsMap[dc.Name]
if !ok {
return nil, fmt.Errorf("oldRRsMap: no zone named %q", dc.Name)
}
zoneName, ok := g.zoneNameMap[dc.Name]
if !ok {
return nil, fmt.Errorf("zoneNameMap: no zone named %q", dc.Name)
}
// first collect keys that have changed
var differ diff.Differ
if !diff2.EnableDiff2 {

View File

@@ -76,17 +76,6 @@ func (c *gcoreProvider) GetNameservers(domain string) ([]*models.Nameserver, err
return models.ToNameservers(defaultNameServerNames)
}
func (c *gcoreProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
existing, err := c.GetZoneRecords(dc.Name)
if err != nil {
return nil, err
}
models.PostProcessRecords(existing)
clean := PrepFoundRecords(existing)
PrepDesiredRecords(dc)
return c.GenerateDomainCorrections(dc, clean)
}
// GetZoneRecords gets the records of a zone and returns them in RecordConfig format.
func (c *gcoreProvider) GetZoneRecords(domain string) (models.Records, error) {
zone, err := c.provider.Zone(c.ctx, domain)
@@ -132,19 +121,6 @@ func (c *gcoreProvider) EnsureZoneExists(domain string) error {
return err
}
// PrepFoundRecords munges any records to make them compatible with
// this provider. Usually this is a no-op.
func PrepFoundRecords(recs models.Records) models.Records {
// If there are records that need to be modified, removed, etc. we
// do it here. Usually this is a no-op.
return recs
}
// PrepDesiredRecords munges any records to best suit this provider.
func PrepDesiredRecords(dc *models.DomainConfig) {
dc.Punycode()
}
func generateChangeMsg(updates []string) string {
return strings.Join(updates, "\n")
}
@@ -154,7 +130,7 @@ func generateChangeMsg(updates []string) string {
// a list of functions to call to actually make the desired
// correction, and a message to output to the user when the change is
// made.
func (c *gcoreProvider) GenerateDomainCorrections(dc *models.DomainConfig, existing models.Records) ([]*models.Correction, error) {
func (c *gcoreProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existing models.Records) ([]*models.Correction, error) {
// Make delete happen earlier than creates & updates.
var corrections []*models.Correction

View File

@@ -178,18 +178,8 @@ func (c *hednsProvider) GetNameservers(_ string) ([]*models.Nameserver, error) {
return models.ToNameservers(defaultNameservers)
}
// GetDomainCorrections returns a list of corrections for the domain.
func (c *hednsProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
err := dc.Punycode()
if err != nil {
return nil, err
}
records, err := c.GetZoneRecords(dc.Name)
if err != nil {
return nil, err
}
// GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records.
func (c *hednsProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, records models.Records) ([]*models.Correction, error) {
// Get the SOA record to get the ZoneID, then remove it from the list.
zoneID := uint64(0)
@@ -203,7 +193,6 @@ func (c *hednsProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models
}
// Normalize
models.PostProcessRecords(prunedRecords)
txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records
// Fallback to legacy mode if diff2 is not enabled, remove when diff1 is deprecated.
@@ -215,7 +204,6 @@ func (c *hednsProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models
func (c *hednsProvider) getDiff1DomainCorrections(dc *models.DomainConfig, zoneID uint64, records models.Records) ([]*models.Correction, error) {
var corrections []*models.Correction
var toCreate, toDelete, toModify diff.Changeset
differ := diff.New(dc)
_, toCreate, toDelete, toModify, err := differ.IncrementalDiff(records)

View File

@@ -69,38 +69,20 @@ func (api *hetznerProvider) EnsureZoneExists(domain string) error {
return api.createZone(domain)
}
// GetDomainCorrections returns the corrections for a domain.
func (api *hetznerProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
dc, err := dc.Copy()
if err != nil {
return nil, err
}
err = dc.Punycode()
if err != nil {
return nil, err
}
// 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) {
domain := dc.Name
// Get existing records
existingRecords, err := api.GetZoneRecords(domain)
if err != nil {
return nil, err
}
// Normalize
models.PostProcessRecords(existingRecords)
txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records
var corrections []*models.Correction
var create, del, modify diff.Changeset
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 := differ.IncrementalDiff(existingRecords)
if err != nil {
return nil, err
}

View File

@@ -57,12 +57,8 @@ func (n *HXClient) GetZoneRecords(domain string) (models.Records, error) {
}
// GetDomainCorrections gathers correctios that would bring n to match dc.
func (n *HXClient) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
err := dc.Punycode()
if err != nil {
return nil, err
}
// 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 {
@@ -71,19 +67,16 @@ func (n *HXClient) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Corr
//checkNSModifications(dc)
// Normalize
models.PostProcessRecords(actual)
txtutil.SplitSingleLongTxt(dc.Records)
var corrections []*models.Correction
var create, del, mod diff.Changeset
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 := differ.IncrementalDiff(actual)
if err != nil {
return nil, err
}

View File

@@ -117,13 +117,9 @@ func soaToString(s soaValues) string {
return fmt.Sprintf("refresh=%d retry=%d expire=%d negativettl=%d ttl=%d", s.Refresh, s.Retry, s.Expire, s.NegativeTTL, s.TTL)
}
func (hp *hostingdeProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
err := dc.Punycode()
if err != nil {
return nil, err
}
zoneChanged := false
// GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records.
func (hp *hostingdeProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, records models.Records) ([]*models.Correction, error) {
var err error
// TTL must be between (inclusive) 1m and 1y (in fact, a little bit more)
for _, r := range dc.Records {
@@ -134,13 +130,14 @@ func (hp *hostingdeProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*m
r.TTL = 31556926
}
}
zoneChanged := false
zone, err := hp.getZone(dc.Name)
if err != nil {
return nil, err
}
records := hp.APIRecordsToStandardRecordsModel(dc.Name, zone.Records)
var create, del, mod diff.Changeset
if !diff2.EnableDiff2 {
_, create, del, mod, err = diff.New(dc).IncrementalDiff(records)
@@ -320,10 +317,10 @@ func firstNonZero(items ...uint32) uint32 {
}
func (hp *hostingdeProvider) GetRegistrarCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
err := dc.Punycode()
if err != nil {
return nil, err
}
// err := dc.Punycode()
// if err != nil {
// return nil, err
// }
found, err := hp.getNameservers(dc.Name)
if err != nil {

View File

@@ -226,32 +226,24 @@ func checkRecords(records models.Records) error {
return nil
}
// GetDomainCorrections finds the currently existing records and returns the corrections required to update them.
func (api *inwxAPI) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
dc.Punycode()
// GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records.
func (api *inwxAPI) GetZoneRecordsCorrections(dc *models.DomainConfig, foundRecords models.Records) ([]*models.Correction, error) {
foundRecords, err := api.GetZoneRecords(dc.Name)
if err != nil {
return nil, err
}
models.PostProcessRecords(foundRecords)
txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records
err = checkRecords(dc.Records)
err := checkRecords(dc.Records)
if err != nil {
return nil, err
}
var corrections []*models.Correction
var create, del, mod diff.Changeset
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 := differ.IncrementalDiff(foundRecords)
if err != nil {
return nil, err
}

View File

@@ -122,33 +122,8 @@ func (api *linodeProvider) GetZoneRecords(domain string) (models.Records, error)
return api.getRecordsForDomain(domainID, domain)
}
// GetDomainCorrections returns the corrections for a domain.
func (api *linodeProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
dc, err := dc.Copy()
if err != nil {
return nil, err
}
dc.Punycode()
if api.domainIndex == nil {
if err := api.fetchDomainList(); err != nil {
return nil, err
}
}
domainID, ok := api.domainIndex[dc.Name]
if !ok {
return nil, fmt.Errorf("'%s' not a zone in Linode account", dc.Name)
}
existingRecords, err := api.getRecordsForDomain(domainID, dc.Name)
if err != nil {
return nil, err
}
// Normalize
models.PostProcessRecords(existingRecords)
// 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) {
// 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
// By experimentation, Linode always rounds up. 300 -> 300, 301 -> 3600.
@@ -157,15 +132,25 @@ func (api *linodeProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*mod
record.TTL = fixTTL(record.TTL)
}
var err error
if api.domainIndex == nil {
if err = api.fetchDomainList(); err != nil {
return nil, err
}
}
domainID, ok := api.domainIndex[dc.Name]
if !ok {
return nil, fmt.Errorf("'%s' not a zone in Linode account", dc.Name)
}
var corrections []*models.Correction
var create, del, modify diff.Changeset
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 := differ.IncrementalDiff(existingRecords)
if err != nil {
return nil, err
}

View File

@@ -156,29 +156,6 @@ func (c *APIClient) ListZones() ([]string, error) {
return zones, nil
}
// NB(tal): To future-proof your code, all new providers should
// implement GetDomainCorrections exactly as you see here
// (byte-for-byte the same). In 3.0
// we plan on using just the individual calls to GetZoneRecords,
// PostProcessRecords, and so on.
//
// Currently every provider does things differently, which prevents
// us from doing things like using GetZoneRecords() of a provider
// to make convertzone work with all providers.
// GetDomainCorrections get the current and existing records,
// post-process them, and generate corrections.
func (c *APIClient) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
existing, err := c.GetZoneRecords(dc.Name)
if err != nil {
return nil, err
}
models.PostProcessRecords(existing)
clean := PrepFoundRecords(existing)
PrepDesiredRecords(dc)
return c.GenerateZoneRecordsCorrections(dc, clean)
}
// GetZoneRecords gathers the DNS records and converts them to
// dnscontrol's format.
func (c *APIClient) GetZoneRecords(domain string) (models.Records, error) {
@@ -235,11 +212,11 @@ func (c *APIClient) GetZoneRecords(domain string) (models.Records, error) {
// PrepFoundRecords munges any records to make them compatible with
// this provider. Usually this is a no-op.
func PrepFoundRecords(recs models.Records) models.Records {
// If there are records that need to be modified, removed, etc. we
// do it here. Usually this is a no-op.
return recs
}
//func PrepFoundRecords(recs models.Records) models.Records {
// If there are records that need to be modified, removed, etc. we
// do it here. Usually this is a no-op.
//return recs
//}
// PrepDesiredRecords munges any records to best suit this provider.
func PrepDesiredRecords(dc *models.DomainConfig) {
@@ -248,8 +225,6 @@ func PrepDesiredRecords(dc *models.DomainConfig) {
// provider. We try to do minimal changes otherwise it gets
// confusing.
dc.Punycode()
recordsToKeep := make([]*models.RecordConfig, 0, len(dc.Records))
for _, rec := range dc.Records {
if rec.Type == "ALIAS" {
@@ -266,9 +241,6 @@ func PrepDesiredRecords(dc *models.DomainConfig) {
printer.Warnf("Loopia does not support TTL > 68 years. Setting %s from %d to 2147483647\n", rec.GetLabelFQDN(), rec.TTL)
rec.TTL = 2147483647
}
// if rec.Type == "TXT" {
// rec.SetTarget("\"" + rec.GetTargetField() + "\"") // FIXME(systemcrash): Should do proper quoting.
// }
// if rec.Type == "NS" && rec.GetLabel() == "@" {
// if !strings.HasSuffix(rec.GetTargetField(), ".loopia.se.") {
// printer.Warnf("Loopia does not support changing apex NS records. Ignoring %s\n", rec.GetTargetField())
@@ -293,18 +265,15 @@ func gatherAffectedLabels(groups map[models.RecordKey][]string) (labels map[stri
return labels, msgs
}
// GenerateZoneRecordsCorrections takes the desired and existing records
// and produces a Correction list. The correction list is simply
// a list of functions to call to actually make the desired
// correction, and a message to output to the user when the change is
// made.
func (c *APIClient) GenerateZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, error) {
// 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) {
if c.Debug {
debugRecords("GenerateZoneRecordsCorrections input:\n", existingRecords)
}
// Normalize
txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records
PrepDesiredRecords(dc)
var corrections []*models.Correction
var keysToUpdate map[models.RecordKey][]string

View File

@@ -94,25 +94,15 @@ func (l *luadnsProvider) GetZoneRecords(domain string) (models.Records, error) {
return existingRecords, nil
}
// GetDomainCorrections returns a list of corrections to update a domain.
func (l *luadnsProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
err := dc.Punycode()
if err != nil {
return nil, err
}
// 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) {
checkNS(dc)
domainID, err := l.getDomainID(dc.Name)
if err != nil {
return nil, err
}
records, err := l.GetZoneRecords(dc.Name)
if err != nil {
return nil, err
}
checkNS(dc)
// Normalize
models.PostProcessRecords(records)
var corrections []*models.Correction
var corrs []*models.Correction

View File

@@ -9,19 +9,17 @@ import (
"github.com/StackExchange/dnscontrol/v3/pkg/txtutil"
)
// GetDomainCorrections gets existing records, diffs them against existing, and returns corrections.
func (client *msdnsProvider) GenerateDomainCorrections(dc *models.DomainConfig, foundRecords models.Records) ([]*models.Correction, error) {
// 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) {
// Normalize
models.PostProcessRecords(foundRecords)
txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records
var corrections []*models.Correction
var creates, dels, modifications diff.Changeset
var err error
if !diff2.EnableDiff2 {
differ := diff.New(dc)
_, creates, dels, modifications, err = differ.IncrementalDiff(foundRecords)
_, creates, dels, modifications, err := differ.IncrementalDiff(foundRecords)
if err != nil {
return nil, err
}

View File

@@ -6,7 +6,6 @@ import (
"github.com/StackExchange/dnscontrol/v3/models"
"github.com/StackExchange/dnscontrol/v3/pkg/printer"
"github.com/StackExchange/dnscontrol/v3/pkg/txtutil"
"github.com/StackExchange/dnscontrol/v3/providers"
)
@@ -70,31 +69,6 @@ func newDNS(config map[string]string, metadata json.RawMessage) (providers.DNSSe
// Section 3: Domain Service Provider (DSP) related functions
// NB(tal): To future-proof your code, all new providers should
// implement GetDomainCorrections exactly as you see here
// (byte-for-byte the same). In 3.0
// we plan on using just the individual calls to GetZoneRecords,
// PostProcessRecords, and so on.
//
// Currently every provider does things differently, which prevents
// us from doing things like using GetZoneRecords() of a provider
// to make convertzone work with all providers.
// GetDomainCorrections get the current and existing records,
// post-process them, and generate corrections.
func (client *msdnsProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
existing, err := client.GetZoneRecords(dc.Name)
if err != nil {
return nil, err
}
models.PostProcessRecords(existing)
txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records
clean := PrepFoundRecords(existing)
PrepDesiredRecords(dc)
return client.GenerateDomainCorrections(dc, clean)
}
// GetZoneRecords gathers the DNS records and converts them to
// dnscontrol's format.
func (client *msdnsProvider) GetZoneRecords(domain string) (models.Records, error) {
@@ -119,24 +93,6 @@ func (client *msdnsProvider) GetZoneRecords(domain string) (models.Records, erro
return existingRecords, nil
}
// PrepFoundRecords munges any records to make them compatible with
// this provider. Usually this is a no-op.
func PrepFoundRecords(recs models.Records) models.Records {
// If there are records that need to be modified, removed, etc. we
// do it here. Usually this is a no-op.
return recs
}
// PrepDesiredRecords munges any records to best suit this provider.
func PrepDesiredRecords(dc *models.DomainConfig) {
// Sort through the dc.Records, eliminate any that can't be
// supported; modify any that need adjustments to work with the
// provider. We try to do minimal changes otherwise it gets
// confusing.
dc.Punycode()
}
// NB(tlim): If we want to implement a registrar, refer to
// http://go.microsoft.com/fwlink/?LinkId=288158
// (Get-DnsServerZoneDelegation) for hints about which PowerShell

View File

@@ -1,10 +1,19 @@
package namecheap
import "github.com/StackExchange/dnscontrol/v3/models"
import (
"github.com/StackExchange/dnscontrol/v3/models"
"github.com/StackExchange/dnscontrol/v3/pkg/rejectif"
)
// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
return nil
a := rejectif.Auditor{}
a.Add("MX", rejectif.MxNull)
a.Add("TXT", rejectif.TxtHasDoubleQuotes)
return a.Audit(records)
}

View File

@@ -126,24 +126,113 @@ func (n *namecheapProvider) GetZoneRecords(domain string) (models.Records, error
return nil, err
}
// namecheap has this really annoying feature where they add some parking records if you have no records.
// This causes a few problems for our purposes, specifically the integration tests.
// lets detect that one case and pretend it is a no-op.
if len(records.Hosts) == 2 {
if records.Hosts[0].Type == "CNAME" &&
strings.Contains(records.Hosts[0].Address, "parkingpage") &&
records.Hosts[1].Type == "URL" {
// return an empty zone
return nil, nil
}
}
// Copying this from GetDomainCorrections. This seems redundent
// with what toRecords() does. Leaving it out.
// for _, r := range records.Hosts {
// if r.Type == "SOA" {
// continue
// }
// rec := &models.RecordConfig{
// Type: r.Type,
// TTL: uint32(r.TTL),
// MxPreference: uint16(r.MXPref),
// Original: r,
// }
// rec.SetLabel(r.Name, dc.Name)
// switch rtype := r.Type; rtype { // #rtype_variations
// case "TXT":
// rec.SetTargetTXT(r.Address)
// case "CAA":
// rec.SetTargetCAAString(r.Address)
// default:
// rec.SetTarget(r.Address)
// }
// actual = append(actual, rec)
// }
return toRecords(records, domain)
}
// GetDomainCorrections returns the corrections for the domain.
func (n *namecheapProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
dc.Punycode()
sld, tld := splitDomain(dc.Name)
var records *nc.DomainDNSGetHostsResult
var err error
doWithRetry(func() error {
records, err = n.client.DomainsDNSGetHosts(sld, tld)
return err
})
if err != nil {
return nil, err
}
// // GetDomainCorrections returns the corrections for the domain.
// func (n *namecheapProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
// dc.Punycode()
// sld, tld := splitDomain(dc.Name)
// var records *nc.DomainDNSGetHostsResult
// var err error
// doWithRetry(func() error {
// records, err = n.client.DomainsDNSGetHosts(sld, tld)
// return err
// })
// if err != nil {
// return nil, err
// }
var actual []*models.RecordConfig
// var actual []*models.RecordConfig
// // namecheap does not allow setting @ NS with basic DNS
// dc.Filter(func(r *models.RecordConfig) bool {
// if r.Type == "NS" && r.GetLabel() == "@" {
// if !strings.HasSuffix(r.GetTargetField(), "registrar-servers.com.") {
// printer.Println("\n", r.GetTargetField(), "Namecheap does not support changing apex NS records. Skipping.")
// }
// return false
// }
// return true
// })
// // namecheap has this really annoying feature where they add some parking records if you have no records.
// // This causes a few problems for our purposes, specifically the integration tests.
// // lets detect that one case and pretend it is a no-op.
// if len(dc.Records) == 0 && len(records.Hosts) == 2 {
// if records.Hosts[0].Type == "CNAME" &&
// strings.Contains(records.Hosts[0].Address, "parkingpage") &&
// records.Hosts[1].Type == "URL" {
// return nil, nil
// }
// }
// for _, r := range records.Hosts {
// if r.Type == "SOA" {
// continue
// }
// rec := &models.RecordConfig{
// Type: r.Type,
// TTL: uint32(r.TTL),
// MxPreference: uint16(r.MXPref),
// Original: r,
// }
// rec.SetLabel(r.Name, dc.Name)
// switch rtype := r.Type; rtype { // #rtype_variations
// case "TXT":
// rec.SetTargetTXT(r.Address)
// case "CAA":
// rec.SetTargetCAAString(r.Address)
// default:
// rec.SetTarget(r.Address)
// }
// actual = append(actual, rec)
// }
// // Normalize
// models.PostProcessRecords(actual)
// return n.GetZoneRecordsCorrections(dc, actual)
// }
// GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records.
func (n *namecheapProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, actual models.Records) ([]*models.Correction, error) {
// namecheap does not allow setting @ NS with basic DNS
dc.Filter(func(r *models.RecordConfig) bool {
@@ -156,50 +245,13 @@ func (n *namecheapProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*mo
return true
})
// namecheap has this really annoying feature where they add some parking records if you have no records.
// This causes a few problems for our purposes, specifically the integration tests.
// lets detect that one case and pretend it is a no-op.
if len(dc.Records) == 0 && len(records.Hosts) == 2 {
if records.Hosts[0].Type == "CNAME" &&
strings.Contains(records.Hosts[0].Address, "parkingpage") &&
records.Hosts[1].Type == "URL" {
return nil, nil
}
}
for _, r := range records.Hosts {
if r.Type == "SOA" {
continue
}
rec := &models.RecordConfig{
Type: r.Type,
TTL: uint32(r.TTL),
MxPreference: uint16(r.MXPref),
Original: r,
}
rec.SetLabel(r.Name, dc.Name)
switch rtype := r.Type; rtype { // #rtype_variations
case "TXT":
rec.SetTargetTXT(r.Address)
case "CAA":
rec.SetTargetCAAString(r.Address)
default:
rec.SetTarget(r.Address)
}
actual = append(actual, rec)
}
// Normalize
models.PostProcessRecords(actual)
var create, delete, modify diff.Changeset
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 := differ.IncrementalDiff(actual)
if err != nil {
return nil, err
}
@@ -245,7 +297,14 @@ func toRecords(result *nc.DomainDNSGetHostsResult, origin string) ([]*models.Rec
MxPreference: uint16(dnsHost.MXPref),
Name: dnsHost.Name,
}
record.PopulateFromString(dnsHost.Type, dnsHost.Address, origin)
record.SetLabel(dnsHost.Name, origin)
switch dnsHost.Type {
case "MX":
record.SetTargetMX(uint16(dnsHost.MXPref), dnsHost.Address)
default:
record.PopulateFromString(dnsHost.Type, dnsHost.Address, origin)
}
records = append(records, &record)
}

View File

@@ -34,14 +34,10 @@ func (n *namedotcomProvider) GetZoneRecords(domain string) (models.Records, erro
return actual, nil
}
// GetDomainCorrections gathers correctios that would bring n to match dc.
func (n *namedotcomProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
dc.Punycode()
// 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) {
actual, err := n.GetZoneRecords(dc.Name)
if err != nil {
return nil, err
}
checkNSModifications(dc)
for _, rec := range dc.Records {
if rec.Type == "ALIAS" {
@@ -49,11 +45,6 @@ func (n *namedotcomProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*m
}
}
checkNSModifications(dc)
// Normalize
models.PostProcessRecords(actual)
var corrections []*models.Correction
var differ diff.Differ
if !diff2.EnableDiff2 {
@@ -193,19 +184,6 @@ func (n *namedotcomProvider) createRecord(rc *models.RecordConfig, domain string
return err
}
// // makeTxt encodes TxtStrings for sending in the CREATE/MODIFY API:
// func encodeTxt(txts []string) string {
// ans := txts[0]
// if len(txts) > 1 {
// ans = ""
// for _, t := range txts {
// ans += `"` + strings.Replace(t, `"`, `\"`, -1) + `"`
// }
// }
// return ans
// }
// finds a string surrounded by quotes that might contain an escaped quote character.
var quotedStringRegexp = regexp.MustCompile(`"((?:[^"\\]|\\.)*)"`)

View File

@@ -53,6 +53,7 @@ func (api *netcupProvider) GetZoneRecords(domain string) (models.Records, error)
for i := range records {
existingRecords[i] = toRecordConfig(domain, &records[i])
}
return existingRecords, nil
}
@@ -67,16 +68,13 @@ func (api *netcupProvider) GetNameservers(domain string) ([]*models.Nameserver,
})
}
// GetDomainCorrections returns the corrections for a domain.
func (api *netcupProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
dc, err := dc.Copy()
if err != nil {
return nil, err
}
dc.Punycode()
// 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) {
domain := dc.Name
// no need for txtutil.SplitSingleLongTxt in function GetDomainCorrections
// txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records
// Setting the TTL is not supported for netcup
for _, r := range dc.Records {
r.TTL = 0
@@ -91,26 +89,14 @@ func (api *netcupProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*mod
}
dc.Records = newRecords
// Check existing set
existingRecords, err := api.GetZoneRecords(domain)
if err != nil {
return nil, err
}
// Normalize
models.PostProcessRecords(existingRecords)
// no need for txtutil.SplitSingleLongTxt in function GetDomainCorrections
// txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records
var corrections []*models.Correction
var create, del, modify diff.Changeset
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 := differ.IncrementalDiff(existingRecords)
if err != nil {
return nil, err
}

View File

@@ -176,31 +176,19 @@ func removeOtherApexNS(dc *models.DomainConfig) {
dc.Records = newList
}
func (n *netlifyProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
// 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) {
err := dc.Punycode()
if err != nil {
return nil, err
}
records, err := n.GetZoneRecords(dc.Name)
if err != nil {
return nil, err
}
// Normalize
models.PostProcessRecords(records)
removeOtherApexNS(dc)
var corrections []*models.Correction
var create, del, modify diff.Changeset
var differ diff.Differ
if !diff2.EnableDiff2 {
differ = diff.New(dc)
} else {
differ = diff.NewCompat(dc)
}
_, create, del, modify, err = differ.IncrementalDiff(records)
_, create, del, modify, err := differ.IncrementalDiff(records)
if err != nil {
return nil, err
}

View File

@@ -154,21 +154,10 @@ func (n *nsone) getDomainCorrectionsDNSSEC(domain, toggleDNSSEC string) *models.
return nil
}
func (n *nsone) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
dc.Punycode()
//dc.CombineMXs()
domain := dc.Name
// 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{}
// Get existing records
existingRecords, err := n.GetZoneRecords(domain)
if err != nil {
return nil, err
}
// Normalize
models.PostProcessRecords(existingRecords)
domain := dc.Name
// add DNSSEC-related corrections
if dnssecCorrections := n.getDomainCorrectionsDNSSEC(domain, dc.AutoDNSSEC); dnssecCorrections != nil {

View File

@@ -202,25 +202,9 @@ func (o *oracleProvider) GetZoneRecords(zone string) (models.Records, error) {
return records, nil
}
func (o *oracleProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
dc, err := dc.Copy()
if err != nil {
return nil, err
}
err = dc.Punycode()
if err != nil {
return nil, err
}
domain := dc.Name
existingRecords, err := o.GetZoneRecords(domain)
if err != nil {
return nil, err
}
// Normalize
models.PostProcessRecords(existingRecords)
// GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records.
func (o *oracleProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, error) {
var err error
txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records
// Ensure we don't emit changes for attempted modification of built-in apex NSs
@@ -241,14 +225,13 @@ func (o *oracleProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*model
}
}
var create, dels, modify diff.Changeset
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 := differ.IncrementalDiff(existingRecords)
if err != nil {
return nil, err
}
@@ -294,7 +277,7 @@ func (o *oracleProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*model
return []*models.Correction{{
Msg: desc,
F: func() error {
return o.patch(createRecords, deleteRecords, domain)
return o.patch(createRecords, deleteRecords, dc.Name)
},
}}, nil
}

View File

@@ -115,26 +115,15 @@ func (c *ovhProvider) GetZoneRecords(domain string) (models.Records, error) {
actual = append(actual, rec)
}
}
return actual, nil
}
func (c *ovhProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
var err error
err = dc.Punycode()
if err != nil {
return nil, err
}
actual, err := c.GetZoneRecords(dc.Name)
if err != nil {
return nil, err
}
// Normalize
models.PostProcessRecords(actual)
// 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 {

View File

@@ -19,7 +19,7 @@ var defaultNameServerNames = []string{
"ns2.packetframe.com",
}
type zone struct {
type zoneInfo struct {
ID string `json:"id"`
Zone string `json:"zone"`
Users []string `json:"users"`
@@ -28,7 +28,7 @@ type zone struct {
type domainResponse struct {
Data struct {
Zones []zone `json:"zones"`
Zones []zoneInfo `json:"zones"`
} `json:"data"`
Message string `json:"message"`
Success bool `json:"success"`
@@ -58,7 +58,7 @@ type domainRecord struct {
}
func (api *packetframeProvider) fetchDomainList() error {
api.domainIndex = map[string]zone{}
api.domainIndex = map[string]zoneInfo{}
dr := &domainResponse{}
endpoint := "dns/zones"
if err := api.get(endpoint, dr); err != nil {

View File

@@ -19,7 +19,7 @@ type packetframeProvider struct {
client *http.Client
baseURL *url.URL
token string
domainIndex map[string]zone
domainIndex map[string]zoneInfo
}
// newPacketframe creates the provider.
@@ -60,19 +60,28 @@ func (api *packetframeProvider) GetNameservers(domain string) ([]*models.Nameser
return models.ToNameservers(defaultNameServerNames)
}
// GetZoneRecords gets the records of a zone and returns them in RecordConfig format.
func (api *packetframeProvider) GetZoneRecords(domain string) (models.Records, error) {
func (api *packetframeProvider) getZone(domain string) (*zoneInfo, error) {
if api.domainIndex == nil {
if err := api.fetchDomainList(); err != nil {
return nil, err
}
}
zone, ok := api.domainIndex[domain+"."]
z, ok := api.domainIndex[domain+"."]
if !ok {
return nil, fmt.Errorf("%q not a zone in Packetframe account", domain)
}
return &z, nil
}
// GetZoneRecords gets the records of a zone and returns them in RecordConfig format.
func (api *packetframeProvider) GetZoneRecords(domain string) (models.Records, error) {
zone, err := api.getZone(domain)
if err != nil {
return nil, fmt.Errorf("no such zone %q in Packetframe account", domain)
}
records, err := api.getRecords(zone.ID)
if err != nil {
return nil, fmt.Errorf("could not load records for domain %q", domain)
@@ -91,48 +100,21 @@ func (api *packetframeProvider) GetZoneRecords(domain string) (models.Records, e
return existingRecords, nil
}
// GetDomainCorrections returns the corrections for a domain.
func (api *packetframeProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
dc, err := dc.Copy()
// 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) {
zone, err := api.getZone(dc.Name)
if err != nil {
return nil, err
}
dc.Punycode()
if api.domainIndex == nil {
if err := api.fetchDomainList(); err != nil {
return nil, err
}
}
zone, ok := api.domainIndex[dc.Name+"."]
if !ok {
return nil, fmt.Errorf("no such zone %q in Packetframe account", dc.Name)
}
records, err := api.getRecords(zone.ID)
if err != nil {
return nil, fmt.Errorf("could not load records for domain %q", dc.Name)
}
existingRecords := make([]*models.RecordConfig, len(records))
for i := range records {
existingRecords[i] = toRc(dc, &records[i])
}
// Normalize
models.PostProcessRecords(existingRecords)
var corrections []*models.Correction
var create, dels, modify diff.Changeset
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 := differ.IncrementalDiff(existingRecords)
if err != nil {
return nil, err
}

View File

@@ -75,26 +75,12 @@ func (c *porkbunProvider) GetNameservers(domain string) ([]*models.Nameserver, e
return models.ToNameservers(defaultNS)
}
// GetDomainCorrections returns the corrections for a domain.
func (c *porkbunProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
dc, err := dc.Copy()
if err != nil {
return nil, err
}
dc.Punycode()
existingRecords, err := c.GetZoneRecords(dc.Name)
if err != nil {
return nil, err
}
// 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) {
// Block changes to NS records for base domain
checkNSModifications(dc)
// Normalize
models.PostProcessRecords(existingRecords)
// Make sure TTL larger than the minimum TTL
for _, record := range dc.Records {
record.TTL = fixTTL(record.TTL)

View File

@@ -2,11 +2,12 @@ package powerdns
import (
"context"
"net/http"
"github.com/StackExchange/dnscontrol/v3/models"
"github.com/StackExchange/dnscontrol/v3/pkg/diff2"
"github.com/mittwald/go-powerdns/apis/zones"
"github.com/mittwald/go-powerdns/pdnshttp"
"net/http"
)
// GetNameservers returns the nameservers for a domain.
@@ -44,19 +45,10 @@ func (dsp *powerdnsProvider) GetZoneRecords(domain string) (models.Records, erro
return curRecords, nil
}
// GetDomainCorrections returns a list of corrections to update a domain.
func (dsp *powerdnsProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
// get current zone records
existing, err := dsp.GetZoneRecords(dc.Name)
if err != nil {
return nil, err
}
// post-process records
if err := dc.Punycode(); err != nil {
return nil, err
}
models.PostProcessRecords(existing)
// 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 {

View File

@@ -158,10 +158,12 @@ func (n None) GetNameservers(string) ([]*models.Nameserver, error) {
// GetZoneRecords gets the records of a zone and returns them in RecordConfig format.
func (n None) GetZoneRecords(domain string) (models.Records, error) {
return nil, fmt.Errorf("not implemented")
// This enables the get-zones subcommand.
// Implement this by extracting the code from GetDomainCorrections into
// a single function. For most providers this should be relatively easy.
return nil, nil
}
// GetZoneRecordsCorrections gets the records of a zone and returns them in RecordConfig format.
func (n None) GetZoneRecordsCorrections(dc *models.DomainConfig, records models.Records) ([]*models.Correction, error) {
return nil, nil
}
// GetDomainCorrections returns corrections to update a domain.

View File

@@ -134,6 +134,8 @@ func (r *route53Provider) ListZones() ([]string, error) {
}
func (r *route53Provider) getZones() error {
// TODO(tlim) This should memoize itself.
if r.zonesByDomain != nil {
return nil
}
@@ -223,6 +225,8 @@ func (r *route53Provider) GetZoneRecords(domain string) (models.Records, error)
}
func (r *route53Provider) getZone(dc *models.DomainConfig) (r53Types.HostedZone, error) {
// TODO(tlim) This should memoize itself.
if err := r.getZones(); err != nil {
return r53Types.HostedZone{}, err
}
@@ -260,19 +264,15 @@ func (r *route53Provider) getZoneRecords(zone r53Types.HostedZone) (models.Recor
return existingRecords, nil
}
func (r *route53Provider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
dc.Punycode()
// GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records.
func (r *route53Provider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, error) {
txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records
zone, err := r.getZone(dc)
if err != nil {
return nil, err
}
existingRecords, err := r.getZoneRecords(zone)
if err != nil {
return nil, err
}
// update zone_id to current zone.id if not specified by the user
for _, want := range dc.Records {
if want.Type == "R53_ALIAS" && want.R53Alias["zone_id"] == "" {
@@ -280,13 +280,14 @@ func (r *route53Provider) GetDomainCorrections(dc *models.DomainConfig) ([]*mode
}
}
// Normalize
models.PostProcessRecords(existingRecords)
txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records
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)

View File

@@ -30,37 +30,19 @@ func (api *rwthProvider) GetNameservers(domain string) ([]*models.Nameserver, er
return models.ToNameservers(RWTHDefaultNs)
}
// GetDomainCorrections returns a list of corretions to execute.
func (api *rwthProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
dc, err := dc.Copy()
if err != nil {
return nil, err
}
err = dc.Punycode()
if err != nil {
return nil, err
}
// GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records.
func (api *rwthProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, error) {
txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records
domain := dc.Name
// Get existing records
existingRecords, err := api.GetZoneRecords(domain)
if err != nil {
return nil, err
}
// Normalize
models.PostProcessRecords(existingRecords)
txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records
var corrections []*models.Correction
var create, del, modify diff.Changeset
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 := differ.IncrementalDiff(existingRecords)
if err != nil {
return nil, err
}

View File

@@ -59,25 +59,25 @@ func (s *softlayerProvider) GetNameservers(domain string) ([]*models.Nameserver,
return models.ToNameservers([]string{"ns1.softlayer.com", "ns2.softlayer.com"})
}
// GetZoneRecords gets the records of a zone and returns them in RecordConfig format.
func (s *softlayerProvider) GetZoneRecords(domain string) (models.Records, error) {
return nil, fmt.Errorf("not implemented")
// This enables the get-zones subcommand.
// Implement this by extracting the code from GetDomainCorrections into
// a single function. For most providers this should be relatively easy.
}
// GetDomainCorrections returns corrections to update a domain.
func (s *softlayerProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
domain, err := s.getDomain(&dc.Name)
// GetZoneRecords gets all the records for domainName and converts
// them to model.RecordConfig.
func (s *softlayerProvider) GetZoneRecords(domainName string) (models.Records, error) {
domain, err := s.getDomain(&domainName)
if err != nil {
return nil, err
}
actual, err := s.getExistingRecords(domain)
if err != nil {
return nil, err
}
return actual, nil
}
// 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) {
domain, err := s.getDomain(&dc.Name)
if err != nil {
return nil, err
}
@@ -120,6 +120,8 @@ func (s *softlayerProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*mo
}
func (s *softlayerProvider) getDomain(name *string) (*datatypes.Dns_Domain, error) {
// FIXME(tlim) Memoize this
domains, err := services.GetAccountService(s.Session).
Filter(filter.Path("domains.name").Eq(name).Build()).
Mask("resourceRecords").
@@ -138,7 +140,7 @@ func (s *softlayerProvider) getDomain(name *string) (*datatypes.Dns_Domain, erro
return &domains[0], nil
}
func (s *softlayerProvider) getExistingRecords(domain *datatypes.Dns_Domain) ([]*models.RecordConfig, error) {
func (s *softlayerProvider) getExistingRecords(domain *datatypes.Dns_Domain) (models.Records, error) {
actual := []*models.RecordConfig{}
for _, record := range domain.ResourceRecords {
@@ -190,9 +192,6 @@ func (s *softlayerProvider) getExistingRecords(domain *datatypes.Dns_Domain) ([]
actual = append(actual, recConfig)
}
// Normalize
models.PostProcessRecords(actual)
return actual, nil
}

View File

@@ -94,25 +94,15 @@ func (n *transipProvider) ListZones() ([]string, error) {
return domains, nil
}
func (n *transipProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
curRecords, err := n.GetZoneRecords(dc.Name)
if err != nil {
return nil, err
}
if err := dc.Punycode(); err != nil {
return nil, err
}
// GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records.
func (n *transipProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, curRecords models.Records) ([]*models.Correction, error) {
removeOtherNS(dc)
models.PostProcessRecords(curRecords)
if !diff2.EnableDiff2 {
corrections, err := n.getCorrectionsUsingOldDiff(dc, curRecords)
return corrections, err
}
corrections, err := n.getCorrectionsUsingDiff2(dc, curRecords)
return corrections, err
}

View File

@@ -109,9 +109,8 @@ func (api *vultrProvider) GetZoneRecords(domain string) (models.Records, error)
return curRecords, nil
}
// GetDomainCorrections gets the corrections for a DomainConfig.
func (api *vultrProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
dc.Punycode()
// 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) {
for _, rec := range dc.Records {
switch rec.Type { // #rtype_variations
@@ -127,23 +126,16 @@ func (api *vultrProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*mode
}
}
curRecords, err := api.GetZoneRecords(dc.Name)
if err != nil {
return nil, err
}
models.PostProcessRecords(curRecords)
var corrections []*models.Correction
if !diff2.EnableDiff2 {
differ := diff.New(dc)
_, create, delete, modify, err := differ.IncrementalDiff(curRecords)
_, create, toDelete, modify, err := differ.IncrementalDiff(curRecords)
if err != nil {
return nil, err
}
for _, mod := range delete {
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),