CHORE: Remove diff1 from codebase (#2575)

This commit is contained in:
Tom Limoncelli 2023-10-22 13:56:13 -04:00 committed by GitHub
parent d3b358470d
commit c91fe6c1c8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
63 changed files with 305 additions and 3214 deletions

View file

@ -91,111 +91,6 @@ jobs:
ENV_CONTEXT: ${{ toJson(env) }}
VARS_CONTEXT: ${{ toJson(vars) }}
SECRETS_CONTEXT: ${{ toJson(secrets) }}
integrtests-diff1:
if: github.ref != 'refs/heads/master' && github.ref != 'refs/heads/main'
needs: integration-test-providers
runs-on: ubuntu-latest
container:
image: golang:1.21
env:
TEST_RESULTS: "/tmp/test-results"
GOTESTSUM_FORMAT: testname
# These providers will be tested if the env variable is set.
# Set it to the domain name to use during the test.
AZURE_DNS_DOMAIN: ${{ vars.AZURE_DNS_DOMAIN }}
BIND_DOMAIN: ${{ vars.BIND_DOMAIN }}
CLOUDFLAREAPI_DOMAIN: ${{ vars.CLOUDFLAREAPI_DOMAIN }}
CLOUDNS_DOMAIN: ${{ vars.CLOUDNS_DOMAIN }}
CSCGLOBAL_DOMAIN: ${{ vars.CSCGLOBAL_DOMAIN }}
DIGITALOCEAN_DOMAIN: ${{ vars.DIGITALOCEAN_DOMAIN }}
GANDI_V5_DOMAIN: ${{ vars.GANDI_V5_DOMAIN }}
GCLOUD_DOMAIN: ${{ vars.GCLOUD_DOMAIN }}
HEDNS_DOMAIN: ${{ vars.HEDNS_DOMAIN }}
HEXONET_DOMAIN: ${{ vars.HEXONET_DOMAIN }}
NAMEDOTCOM_DOMAIN: ${{ vars.NAMEDOTCOM_DOMAIN }}
NS1_DOMAIN: ${{ vars.NS1_DOMAIN }}
POWERDNS_DOMAIN: ${{ vars.POWERDNS_DOMAIN }}
ROUTE53_DOMAIN: ${{ vars.ROUTE53_DOMAIN }}
TRANSIP_DOMAIN: ${{ vars.TRANSIP_DOMAIN }}
# The above providers have additional env variables they
# need for credentials and such.
AZURE_DNS_CLIENT_ID: ${{ secrets.AZURE_DNS_CLIENT_ID }}
AZURE_DNS_CLIENT_SECRET: ${{ secrets.AZURE_DNS_CLIENT_SECRET }}
AZURE_DNS_RESOURCE_GROUP: ${{ secrets.AZURE_DNS_RESOURCE_GROUP }}
AZURE_DNS_SUBSCRIPTION_ID: ${{ secrets.AZURE_DNS_SUBSCRIPTION_ID }}
AZURE_DNS_TENANT_ID: ${{ secrets.AZURE_DNS_TENANT_ID }}
CLOUDFLAREAPI_ACCOUNTID: ${{ secrets.CLOUDFLAREAPI_ACCOUNTID }}
CLOUDFLAREAPI_TOKEN: ${{ secrets.CLOUDFLAREAPI_TOKEN }}
CLOUDNS_AUTH_ID: ${{ secrets.CLOUDNS_AUTH_ID }}
CLOUDNS_AUTH_PASSWORD: ${{ secrets.CLOUDNS_AUTH_PASSWORD }}
CSCGLOBAL_APIKEY: ${{ secrets.CSCGLOBAL_APIKEY }}
CSCGLOBAL_USERTOKEN: ${{ secrets.CSCGLOBAL_USERTOKEN }}
DIGITALOCEAN_TOKEN: ${{ secrets.DIGITALOCEAN_TOKEN }}
GANDI_V5_APIKEY: ${{ secrets.GANDI_V5_APIKEY }}
GCLOUD_EMAIL: ${{ secrets.GCLOUD_EMAIL }}
GCLOUD_PRIVATEKEY: ${{ secrets.GCLOUD_PRIVATEKEY }}
GCLOUD_PROJECT: ${{ secrets.GCLOUD_PROJECT }}
GCLOUD_TYPE: ${{ secrets.GCLOUD_TYPE }}
HEDNS_PASSWORD: ${{ secrets.HEDNS_PASSWORD }}
HEDNS_TOTP_SECRET: ${{ secrets.HEDNS_TOTP_SECRET }}
HEDNS_USERNAME: ${{ secrets.HEDNS_USERNAME }}
HEXONET_ENTITY: ${{ secrets.HEXONET_ENTITY }}
HEXONET_PW: ${{ secrets.HEXONET_PW }}
HEXONET_UID: ${{ secrets.HEXONET_UID }}
NAMEDOTCOM_KEY: ${{ secrets.NAMEDOTCOM_KEY }}
NAMEDOTCOM_URL: ${{ secrets.NAMEDOTCOM_URL }}
NAMEDOTCOM_USER: ${{ secrets.NAMEDOTCOM_USER }}
NS1_TOKEN: ${{ secrets.NS1_TOKEN }}
POWERDNS_APIKEY: ${{ secrets.POWERDNS_APIKEY }}
POWERDNS_APIURL: ${{ secrets.POWERDNS_APIURL }}
POWERDNS_SERVERNAME: ${{ secrets.POWERDNS_SERVERNAME }}
ROUTE53_KEY: ${{ secrets.ROUTE53_KEY }}
ROUTE53_KEY_ID: ${{ secrets.ROUTE53_KEY_ID }}
TRANSIP_ACCOUNT_NAME: ${{ secrets.TRANSIP_ACCOUNT_NAME }}
TRANSIP_PRIVATE_KEY: ${{ secrets.TRANSIP_PRIVATE_KEY }}
concurrency: ${{ matrix.provider }}
strategy:
fail-fast: false
matrix:
provider: ${{ fromJson(needs.integration-test-providers.outputs.integration_test_providers )}}
steps:
- uses: actions/checkout@v4
- run: mkdir -p "$TEST_RESULTS"
- name: restore_cache
uses: actions/cache@v3.3.2
with:
key: linux-go-${{ hashFiles('go.sum') }}-${{ env.cache-key }}
restore-keys: linux-go-${{ hashFiles('go.sum') }}-${{ env.cache-key }}
path: ${{ env.go-mod-path }}
- name: Run integration tests for ${{ matrix.provider }} provider
run: |-
if [ -z "$${{ matrix.provider }}_DOMAIN" ] ; then
echo "Skip test for ${{ matrix.provider }} provider"
else
go install gotest.tools/gotestsum@latest
gotestsum --junitfile ${TEST_RESULTS}/gotestsum-report.xml -- -timeout 30m -v -verbose -provider ${{ matrix.provider }} -cfworkers=false
fi
working-directory: integrationTest
- uses: actions/upload-artifact@v3.1.3
with:
path: "/tmp/test-results"
integrtests-diff2:
if: github.ref != 'refs/heads/master' && github.ref != 'refs/heads/main'
runs-on: ubuntu-latest
@ -203,7 +98,6 @@ jobs:
image: golang:1.21
needs:
- integration-test-providers
- integrtests-diff1
env:
TEST_RESULTS: "/tmp/test-results"
GOTESTSUM_FORMAT: testname
@ -295,7 +189,7 @@ jobs:
run: |-
go install gotest.tools/gotestsum@latest
if [ -n "$${{ matrix.provider }}_DOMAIN" ] ; then
gotestsum --junitfile ${TEST_RESULTS}/gotestsum-report.xml -- -timeout 30m -v -verbose -provider ${{ matrix.provider }} -cfworkers=false -diff2
gotestsum --junitfile ${TEST_RESULTS}/gotestsum-report.xml -- -timeout 30m -v -verbose -provider ${{ matrix.provider }} -cfworkers=false
else
echo "Skip test for ${{ matrix.provider }} provider"
fi

View file

@ -65,14 +65,17 @@ func Run(v string) int {
Destination: &js.EnableFetch,
},
&cli.BoolFlag{
Name: "diff2",
Usage: "Enable replacement diff algorithm",
Destination: &diff2.EnableDiff2,
Value: true,
Name: "diff2",
Usage: "Obsolete flag. Will be removed in v5 or later",
Hidden: true,
Action: func(ctx *cli.Context, v bool) error {
obsoleteDiff2FlagUsed = true
return nil
},
},
&cli.BoolFlag{
Name: "disableordering",
Usage: "Disables the dns ordering part of the diff2 package",
Usage: "Disables update reordering",
Destination: &diff2.DisableOrdering,
},
&cli.BoolFlag{

View file

@ -12,7 +12,6 @@ import (
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/bindserial"
"github.com/StackExchange/dnscontrol/v4/pkg/credsfile"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/pkg/nameservers"
"github.com/StackExchange/dnscontrol/v4/pkg/normalize"
"github.com/StackExchange/dnscontrol/v4/pkg/notifications"
@ -129,6 +128,8 @@ func Push(args PushArgs) error {
return run(args.PreviewArgs, true, args.Interactive, printer.DefaultPrinter, &args.Report)
}
var obsoleteDiff2FlagUsed = false
// run is the main routine common to preview/push
func run(args PreviewArgs, push bool, interactive bool, out printer.CLI, report *string) error {
// TODO: make truly CLI independent. Perhaps return results on a channel as they occur
@ -136,10 +137,8 @@ func run(args PreviewArgs, push bool, interactive bool, out printer.CLI, report
// This is a hack until we have the new printer replacement.
printer.SkinnyReport = !args.Full
if diff2.EnableDiff2 {
printer.Println("INFO: Diff2 algorithm in use. Welcome to the future!")
} else {
printer.Println("WARNING: Diff1 algorithm in use. Please upgrade to diff2 (`dnscontrol --diff2=true preview`) as diff1 will go away after 2023-07-05. See https://github.com/StackExchange/dnscontrol/issues/2262")
if obsoleteDiff2FlagUsed {
printer.Println("WARNING: Please remove obsolete --diff2 flag. This will be an error in v5 or later. See https://github.com/StackExchange/dnscontrol/issues/2262")
}
cfg, err := GetDNSConfig(args.GetDNSConfigArgs)
@ -237,15 +236,13 @@ func run(args PreviewArgs, push bool, interactive bool, out printer.CLI, report
}
reports, corrections, err := zonerecs.CorrectZoneRecords(provider.Driver, domain)
printReports(domain.Name, provider.Name, reports, out, push, notifier)
out.EndProvider(provider.Name, len(corrections), err)
if err != nil {
anyErrors = true
return
}
totalCorrections += len(corrections)
// When diff1 goes away, the call to printReports() should be moved to HERE.
//printReports(domain.Name, provider.Name, reports, out, push, notifier)
printReports(domain.Name, provider.Name, reports, out, push, notifier)
reportItems = append(reportItems, ReportItem{
Domain: domain.Name,
Corrections: len(corrections),

View file

@ -1245,85 +1245,7 @@ declare function IGNORE(labelSpec: string, typeSpec?: string, targetSpec?: strin
/**
* `IGNORE_NAME(a)` is the same as `IGNORE(a, "*", "*")`.
*
* ## Legacy mode ("diff1")
*
* When `--diff2=false` is used to revert to the old "diff1" algorithm, `IGNORE_NAME()` behaves as follows:
*
* WARNING: The `IGNORE_*` family of functions is risky to use. The code
* is brittle and has subtle bugs. Use at your own risk. Do not use these
* commands with `D_EXTEND()`.
*
* `IGNORE_NAME` can be used to ignore some records present in zone.
* Records of that name will be completely ignored. An optional `rTypes` may be specified as a comma separated list to only ignore records of the given type, e.g. `"A"`, `"A,CNAME"`, `"A, MX, CNAME"`. If `rTypes` is omitted or is `"*"` all record types matching the name will be ignored.
*
* `IGNORE_NAME` is like `NO_PURGE` except it acts only on some specific records instead of the whole zone.
*
* Technically `IGNORE_NAME` is a promise that DNSControl will not add, change, or delete records at a given label. This permits another entity to "own" that label.
*
* `IGNORE_NAME` is generally used in very specific situations:
*
* * Some records are managed by some other system and DNSControl is only used to manage some records and/or keep them updated. For example a DNS `A` record that is managed by a dynamic DNS client, or by Kubernetes External DNS, but DNSControl is used to manage the rest of the zone. In this case we don't want DNSControl to try to delete the externally managed record.
* * To work-around a pseudo record type that is not supported by DNSControl. For example some providers have a fake DNS record type called "URL" which creates a redirect. DNSControl normally deletes these records because it doesn't understand them. `IGNORE_NAME` will leave those records alone.
*
* In this example, DNSControl will insert/update the "baz.example.com" record but will leave unchanged the "foo.example.com" and "bar.example.com" ones.
*
* ```javascript
* D("example.com", REG_MY_PROVIDER, DnsProvider(DSP_MY_PROVIDER),
* IGNORE_NAME("foo"), // ignore all record types for name foo
* IGNORE_NAME("baz", "*"), // ignore all record types for name baz
* IGNORE_NAME("bar", "A,MX"), // ignore only A and MX records for name bar
* CNAME("bar", "www"), // CNAME is not ignored
* A("baz", "1.2.3.4")
* );
* ```
*
* `IGNORE_NAME` also supports glob patterns in the style of the [gobwas/glob](https://github.com/gobwas/glob) library. All of
* the following patterns will work:
*
* * `IGNORE_NAME("*.foo")` will ignore all records in the style of `bar.foo`, but will not ignore records using a double
* subdomain, such as `foo.bar.foo`.
* * `IGNORE_NAME("**.foo")` will ignore all subdomains of `foo`, including double subdomains.
* * `IGNORE_NAME("?oo")` will ignore all records of three symbols ending in `oo`, for example `foo` and `zoo`. It will
* not match `.`
* * `IGNORE_NAME("[abc]oo")` will ignore records `aoo`, `boo` and `coo`. `IGNORE_NAME("[a-c]oo")` is equivalent.
* * `IGNORE_NAME("[!abc]oo")` will ignore all three symbol records ending in `oo`, except for `aoo`, `boo`, `coo`. `IGNORE_NAME("[!a-c]oo")` is equivalent.
* * `IGNORE_NAME("{bar,[fz]oo}")` will ignore `bar`, `foo` and `zoo`.
* * `IGNORE_NAME("\\*.foo")` will ignore the literal record `*.foo`.
*
* # Caveats
*
* It is considered as an error to try to manage an ignored record.
* Ignoring a label is a promise that DNSControl won't meddle with
* anything at a particular label, therefore DNSControl prevents you from
* adding records at a label that is `IGNORE_NAME`'ed.
*
* Use `IGNORE_NAME("@")` to ignore at the domain's apex. Most providers
* insert magic or unchangeable records at the domain's apex; usually `NS`
* and `SOA` records. DNSControl treats them specially.
*
* # Errors
*
* * `trying to update/add IGNORE_NAME'd record: foo CNAME`
*
* This means you have both ignored `foo` and included a record (in this
* case, a CNAME) to update it. This is an error because `IGNORE_NAME`
* is a promise not to modify records at a certain label so that others
* may have free reign there. Therefore, DNSControl prevents you from
* modifying that label.
*
* The `foo CNAME` at the end of the message indicates the label name
* (`foo`) and the type of record (`CNAME`) that your dnsconfig.js file
* is trying to insert.
*
* You can override this error by adding the
* `IGNORE_NAME_DISABLE_SAFETY_CHECK` flag to the record.
*
* TXT("vpn", "this thing", IGNORE_NAME_DISABLE_SAFETY_CHECK)
*
* Disabling this safety check creates two risks:
*
* 1. Two owners (DNSControl and some other entity) toggling a record between two settings.
* 2. The other owner wiping all records at this label, which won't be noticed until the next time DNSControl is run.
* `IGNORE_NAME(a, b)` is the same as `IGNORE(a, b, "*")`.
*
* @see https://docs.dnscontrol.org/language-reference/domain-modifiers/ignore_name
*/
@ -1334,40 +1256,6 @@ declare function IGNORE_NAME(pattern: string, rTypes?: string): DomainModifier;
*
* `IGNORE_TARGET_NAME(target, rtype)` is the same as `IGNORE("*", rtype, target)`.
*
* ## Legacy mode ("diff1")
*
* When `--diff2=false` is used to revert to the old "diff1" algorithm, `IGNORE_NAME()` behaves as follows:
*
* WARNING: The `IGNORE_*` family of functions is risky to use. The code
* is brittle and has subtle bugs. Use at your own risk. Do not use these
* commands with `D_EXTEND()` or use it at the domain apex.
*
* IGNORE_TARGET can be used to ignore some records present in zone based on the record's target and type. IGNORE_TARGET currently only supports CNAME record types.
*
* IGNORE_TARGET is like NO_PURGE except it acts only on some specific records instead of the whole zone.
*
* IGNORE_TARGET is generally used in very specific situations:
*
* * Some records are managed by some other system and DNSControl is only used to manage some records and/or keep them updated. For example a DNS record that is created by AWS Certificate Manager for validation, but DNSControl is used to manage the rest of the zone. In this case we don't want DNSControl to try to delete the externally managed record.
*
* In this example, DNSControl will insert/update the "baz.example.com" record but will leave unchanged a CNAME to "foo.acm-validations.aws" record.
*
* ```javascript
* D("example.com", REG_MY_PROVIDER, DnsProvider(DSP_MY_PROVIDER),
* IGNORE_TARGET("**.acm-validations.aws.", "CNAME"),
* A("baz", "1.2.3.4")
* );
* ```
*
* IGNORE_TARGET also supports glob patterns in the style of the [gobwas/glob](https://github.com/gobwas/glob#example) library. Some example patterns:
*
* * `IGNORE_TARGET("example.com", "CNAME")` will ignore all CNAME records with targets of exactly `example.com`.
* * `IGNORE_TARGET("*.foo", "CNAME")` will ignore all CNAME records with targets in the style of `bar.foo`, but will not ignore records with targets using a double subdomain, such as `foo.bar.foo`.
* * `IGNORE_TARGET("**.bar", "CNAME")` will ignore all CNAME records with target subdomains of `bar`, including double subdomains such as `www.foo.bar`.
* * `IGNORE_TARGET("dev.*.foo", "CNAME")` will ignore all CNAME records with targets in the style of `dev.bar.foo`, but will not ignore records with targets using a double subdomain, such as `dev.foo.bar.foo`.
*
* It is considered as an error to try to manage an ignored record.
*
* @see https://docs.dnscontrol.org/language-reference/domain-modifiers/ignore_target
*/
declare function IGNORE_TARGET(pattern: string, rType: string): DomainModifier;

View file

@ -50,7 +50,7 @@ only run if they have access to the secrets they will need.
# How it works
Tests are executed if `*_DOMAIN` exists where `*` is the name of the provider. If the value is empty or
unset, the test is skipped.
unset, the test is skipped.
For example, if a provider is called `FANCYDNS`, there must
be a secret called `FANCYDNS_DOMAIN`.
@ -79,7 +79,7 @@ The line looks something like:
2. Add your providers `_DOMAIN` env variable:
Add it to the `env` section of `integrtests-diff1` and again in `integrtests-diff2`.
Add it to the `env` section of `integrtests-diff2`.
For example, the entry for BIND looks like:

View file

@ -10,86 +10,4 @@ parameter_types:
`IGNORE_NAME(a)` is the same as `IGNORE(a, "*", "*")`.
## Legacy mode ("diff1")
When `--diff2=false` is used to revert to the old "diff1" algorithm, `IGNORE_NAME()` behaves as follows:
{% hint style="warning" %}
**WARNING**: The `IGNORE_*` family of functions is risky to use. The code
is brittle and has subtle bugs. Use at your own risk. Do not use these
commands with `D_EXTEND()`.
{% endhint %}
`IGNORE_NAME` can be used to ignore some records present in zone.
Records of that name will be completely ignored. An optional `rTypes` may be specified as a comma separated list to only ignore records of the given type, e.g. `"A"`, `"A,CNAME"`, `"A, MX, CNAME"`. If `rTypes` is omitted or is `"*"` all record types matching the name will be ignored.
`IGNORE_NAME` is like `NO_PURGE` except it acts only on some specific records instead of the whole zone.
Technically `IGNORE_NAME` is a promise that DNSControl will not add, change, or delete records at a given label. This permits another entity to "own" that label.
`IGNORE_NAME` is generally used in very specific situations:
* Some records are managed by some other system and DNSControl is only used to manage some records and/or keep them updated. For example a DNS `A` record that is managed by a dynamic DNS client, or by Kubernetes External DNS, but DNSControl is used to manage the rest of the zone. In this case we don't want DNSControl to try to delete the externally managed record.
* To work-around a pseudo record type that is not supported by DNSControl. For example some providers have a fake DNS record type called "URL" which creates a redirect. DNSControl normally deletes these records because it doesn't understand them. `IGNORE_NAME` will leave those records alone.
In this example, DNSControl will insert/update the "baz.example.com" record but will leave unchanged the "foo.example.com" and "bar.example.com" ones.
{% code title="dnsconfig.js" %}
```javascript
D("example.com", REG_MY_PROVIDER, DnsProvider(DSP_MY_PROVIDER),
IGNORE_NAME("foo"), // ignore all record types for name foo
IGNORE_NAME("baz", "*"), // ignore all record types for name baz
IGNORE_NAME("bar", "A,MX"), // ignore only A and MX records for name bar
CNAME("bar", "www"), // CNAME is not ignored
A("baz", "1.2.3.4")
);
```
{% endcode %}
`IGNORE_NAME` also supports glob patterns in the style of the [gobwas/glob](https://github.com/gobwas/glob) library. All of
the following patterns will work:
* `IGNORE_NAME("*.foo")` will ignore all records in the style of `bar.foo`, but will not ignore records using a double
subdomain, such as `foo.bar.foo`.
* `IGNORE_NAME("**.foo")` will ignore all subdomains of `foo`, including double subdomains.
* `IGNORE_NAME("?oo")` will ignore all records of three symbols ending in `oo`, for example `foo` and `zoo`. It will
not match `.`
* `IGNORE_NAME("[abc]oo")` will ignore records `aoo`, `boo` and `coo`. `IGNORE_NAME("[a-c]oo")` is equivalent.
* `IGNORE_NAME("[!abc]oo")` will ignore all three symbol records ending in `oo`, except for `aoo`, `boo`, `coo`. `IGNORE_NAME("[!a-c]oo")` is equivalent.
* `IGNORE_NAME("{bar,[fz]oo}")` will ignore `bar`, `foo` and `zoo`.
* `IGNORE_NAME("\\*.foo")` will ignore the literal record `*.foo`.
# Caveats
It is considered as an error to try to manage an ignored record.
Ignoring a label is a promise that DNSControl won't meddle with
anything at a particular label, therefore DNSControl prevents you from
adding records at a label that is `IGNORE_NAME`'ed.
Use `IGNORE_NAME("@")` to ignore at the domain's apex. Most providers
insert magic or unchangeable records at the domain's apex; usually `NS`
and `SOA` records. DNSControl treats them specially.
# Errors
* `trying to update/add IGNORE_NAME'd record: foo CNAME`
This means you have both ignored `foo` and included a record (in this
case, a CNAME) to update it. This is an error because `IGNORE_NAME`
is a promise not to modify records at a certain label so that others
may have free reign there. Therefore, DNSControl prevents you from
modifying that label.
The `foo CNAME` at the end of the message indicates the label name
(`foo`) and the type of record (`CNAME`) that your dnsconfig.js file
is trying to insert.
You can override this error by adding the
`IGNORE_NAME_DISABLE_SAFETY_CHECK` flag to the record.
TXT("vpn", "this thing", IGNORE_NAME_DISABLE_SAFETY_CHECK)
Disabling this safety check creates two risks:
1. Two owners (DNSControl and some other entity) toggling a record between two settings.
2. The other owner wiping all records at this label, which won't be noticed until the next time DNSControl is run.
`IGNORE_NAME(a, b)` is the same as `IGNORE(a, b, "*")`.

View file

@ -11,41 +11,3 @@ parameter_types:
`IGNORE_TARGET_NAME(target)` is the same as `IGNORE("*", "*", target)`.
`IGNORE_TARGET_NAME(target, rtype)` is the same as `IGNORE("*", rtype, target)`.
## Legacy mode ("diff1")
When `--diff2=false` is used to revert to the old "diff1" algorithm, `IGNORE_NAME()` behaves as follows:
{% hint style="warning" %}
**WARNING**: The `IGNORE_*` family of functions is risky to use. The code
is brittle and has subtle bugs. Use at your own risk. Do not use these
commands with `D_EXTEND()` or use it at the domain apex.
{% endhint %}
IGNORE_TARGET can be used to ignore some records present in zone based on the record's target and type. IGNORE_TARGET currently only supports CNAME record types.
IGNORE_TARGET is like NO_PURGE except it acts only on some specific records instead of the whole zone.
IGNORE_TARGET is generally used in very specific situations:
* Some records are managed by some other system and DNSControl is only used to manage some records and/or keep them updated. For example a DNS record that is created by AWS Certificate Manager for validation, but DNSControl is used to manage the rest of the zone. In this case we don't want DNSControl to try to delete the externally managed record.
In this example, DNSControl will insert/update the "baz.example.com" record but will leave unchanged a CNAME to "foo.acm-validations.aws" record.
{% code title="dnsconfig.js" %}
```javascript
D("example.com", REG_MY_PROVIDER, DnsProvider(DSP_MY_PROVIDER),
IGNORE_TARGET("**.acm-validations.aws.", "CNAME"),
A("baz", "1.2.3.4")
);
```
{% endcode %}
IGNORE_TARGET also supports glob patterns in the style of the [gobwas/glob](https://github.com/gobwas/glob#example) library. Some example patterns:
* `IGNORE_TARGET("example.com", "CNAME")` will ignore all CNAME records with targets of exactly `example.com`.
* `IGNORE_TARGET("*.foo", "CNAME")` will ignore all CNAME records with targets in the style of `bar.foo`, but will not ignore records with targets using a double subdomain, such as `foo.bar.foo`.
* `IGNORE_TARGET("**.bar", "CNAME")` will ignore all CNAME records with target subdomains of `bar`, including double subdomains such as `www.foo.bar`.
* `IGNORE_TARGET("dev.*.foo", "CNAME")` will ignore all CNAME records with targets in the style of `dev.bar.foo`, but will not ignore records with targets using a double subdomain, such as `dev.foo.bar.foo`.
It is considered as an error to try to manage an ignored record.

View file

@ -130,7 +130,7 @@ find * -name \*.bak -delete
GHA is configured to run an integration test for any provider listed in the "provider" list. However the test is skipped if the `*_DOMAIN` variable is not set. For example, the Google Cloud provider integration test is only run if `GCLOUD_DOMAIN` is set.
* Q: Where is the list of providers to run integration tests on?
* A: In `.github/workflows/build.yml`: (1) the "PROVIDERS" list, (2) the `integrtests-diff1` section, (3) the `integrtests-diff2` section.
* A: In `.github/workflows/build.yml`: (1) the "PROVIDERS" list, (2) the `integrtests-diff2` section.
* Q: Where are non-secret environment variables stored?
* A: GHA calls them "Variables". Update them here: https://github.com/StackExchange/dnscontrol/settings/variables/actions
@ -143,7 +143,7 @@ GHA is configured to run an integration test for any provider listed in the "pro
1. Edit `.github/workflows/build.yml`
2. Add the `FOO_DOMAIN` variable name of the provider to the "PROVIDERS" list.
3. Set the `FOO_DOMAIN` variables in GHA via https://github.com/StackExchange/dnscontrol/settings/variables/actions
4. All other variables should be stored as secrets (for consistency). Add them to both the `integrtests-diff1` section and the `integrtests-diff2` section.
4. All other variables should be stored as secrets (for consistency). Add them to the `integrtests-diff2` section.
Set them in GHA via https://github.com/StackExchange/dnscontrol/settings/secrets/actions
### How do I add a "bring your own keys" integration test?

View file

@ -12,7 +12,6 @@ import (
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/credsfile"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/pkg/nameservers"
"github.com/StackExchange/dnscontrol/v4/pkg/zonerecs"
"github.com/StackExchange/dnscontrol/v4/providers"
@ -31,7 +30,6 @@ var enableCFWorkers = flag.Bool("cfworkers", true, "Set false to disable CF work
func init() {
testing.Init()
flag.BoolVar(&diff2.EnableDiff2, "diff2", false, "enable diff2")
flag.Parse()
}
@ -139,11 +137,6 @@ func getDomainConfigWithNameservers(t *testing.T, prv providers.DNSServiceProvid
// error explaining why it is not.
func testPermitted(t *testing.T, p string, f TestGroup) error {
// Does this test require "diff2"?
if f.diff2only && !diff2.EnableDiff2 {
return fmt.Errorf("test for diff2 only")
}
// not() and only() can't be mixed.
if len(f.only) != 0 && len(f.not) != 0 {
return fmt.Errorf("invalid filter: can't mix not() and only()")
@ -223,8 +216,6 @@ func makeChanges(t *testing.T, prv providers.DNSServiceProvider, dc *models.Doma
// records (A or AAAA)
dom.Records = append(dom.Records, a("ns."+domainName+".", "9.8.7.6"))
}
dom.IgnoredNames = tst.IgnoredNames
dom.IgnoredTargets = tst.IgnoredTargets
dom.Unmanaged = tst.Unmanaged
dom.UnmanagedUnsafe = tst.UnmanagedUnsafe
models.PostProcessRecords(dom.Records)
@ -442,14 +433,11 @@ type TestGroup struct {
not []string
trueflags []bool
tests []*TestCase
diff2only bool
}
type TestCase struct {
Desc string
Records []*models.RecordConfig
IgnoredNames []*models.IgnoreName
IgnoredTargets []*models.IgnoreTarget
Unmanaged []*models.UnmanagedConfig
UnmanagedUnsafe bool // DISABLE_IGNORE_SAFETY_CHECK
Changeless bool // set to true if any changes would be an error
@ -467,11 +455,6 @@ func (tc *TestCase) UnsafeIgnore() *TestCase {
return tc
}
func (tg *TestGroup) Diff2Only() *TestGroup {
tg.diff2only = true
return tg
}
func SetLabel(r *models.RecordConfig, label, domain string) {
r.Name = label
r.NameFQDN = dnsutil.AddOrigin(label, "**current-domain**")
@ -546,29 +529,11 @@ func ds(name string, keyTag uint16, algorithm, digestType uint8, digest string)
}
func ignoreName(labelSpec string) *models.RecordConfig {
r := &models.RecordConfig{
Type: "IGNORE_NAME",
Metadata: map[string]string{},
}
// diff1
SetLabel(r, labelSpec, "**current-domain**")
// diff2
r.Metadata["ignore_LabelPattern"] = labelSpec
return r
return ignore(labelSpec, "*", "*")
}
func ignoreTarget(targetSpec string, typeSpec string) *models.RecordConfig {
r := &models.RecordConfig{
Type: "IGNORE_TARGET",
Metadata: map[string]string{},
}
// diff1
r.SetTarget(typeSpec)
SetLabel(r, targetSpec, "**current-domain**")
// diff2
r.Metadata["ignore_RTypePattern"] = typeSpec
r.Metadata["ignore_TargetPattern"] = typeSpec
return r
return ignore("*", "*", targetSpec)
}
func ignore(labelSpec string, typeSpec string, targetSpec string) *models.RecordConfig {
@ -576,9 +541,7 @@ func ignore(labelSpec string, typeSpec string, targetSpec string) *models.Record
Type: "IGNORE",
Metadata: map[string]string{},
}
if r.Metadata == nil {
r.Metadata = map[string]string{}
}
r.Metadata["ignore_LabelPattern"] = labelSpec
r.Metadata["ignore_RTypePattern"] = typeSpec
r.Metadata["ignore_TargetPattern"] = targetSpec
@ -716,50 +679,24 @@ func testgroup(desc string, items ...interface{}) *TestGroup {
func tc(desc string, recs ...*models.RecordConfig) *TestCase {
var records []*models.RecordConfig
var ignoredNames []*models.IgnoreName
var ignoredTargets []*models.IgnoreTarget
var unmanagedItems []*models.UnmanagedConfig
for _, r := range recs {
switch r.Type {
case "IGNORE":
// diff1:
ignoredNames = append(ignoredNames, &models.IgnoreName{
Pattern: r.Metadata["ignore_LabelPattern"],
Types: r.Metadata["ignore_RTypePattern"],
})
// diff2:
unmanagedItems = append(unmanagedItems, &models.UnmanagedConfig{
LabelPattern: r.Metadata["ignore_LabelPattern"],
RTypePattern: r.Metadata["ignore_RTypePattern"],
TargetPattern: r.Metadata["ignore_TargetPattern"],
})
continue
case "IGNORE_NAME":
ignoredNames = append(ignoredNames, &models.IgnoreName{Pattern: r.GetLabel(), Types: r.GetTargetField()})
unmanagedItems = append(unmanagedItems, &models.UnmanagedConfig{
LabelPattern: r.GetLabel(),
RTypePattern: r.GetTargetField(),
})
continue
case "IGNORE_TARGET":
ignoredTargets = append(ignoredTargets, &models.IgnoreTarget{
Pattern: r.GetLabel(),
Type: r.GetTargetField(),
})
unmanagedItems = append(unmanagedItems, &models.UnmanagedConfig{
RTypePattern: r.GetTargetField(),
TargetPattern: r.GetLabel(),
})
default:
records = append(records, r)
}
}
return &TestCase{
Desc: desc,
Records: records,
IgnoredNames: ignoredNames,
IgnoredTargets: ignoredTargets,
Unmanaged: unmanagedItems,
Desc: desc,
Records: records,
Unmanaged: unmanagedItems,
}
}
@ -1914,7 +1851,7 @@ func makeTests(t *testing.T) []*TestGroup {
tc("ignore manytypes",
ignore("", "A,TXT", ""),
).ExpectNoChanges(),
).Diff2Only(),
),
testgroup("IGNORE apex",
tc("Create some records",
@ -1935,7 +1872,7 @@ func makeTests(t *testing.T) []*TestGroup {
tc("ignore manytypes",
ignore("", "A,TXT", ""),
).ExpectNoChanges().UnsafeIgnore(),
).Diff2Only(),
),
// Legacy IGNORE_NAME and IGNORE_TARGET tests.
@ -1968,7 +1905,7 @@ func makeTests(t *testing.T) []*TestGroup {
ignoreName("*.foo"),
a("bar", "1.2.3.4"),
),
).Diff2Only(),
),
testgroup("IGNORE_NAME apex",
tc("Create some records",
@ -1989,7 +1926,7 @@ func makeTests(t *testing.T) []*TestGroup {
a("bar", "2.4.6.8"),
a("added", "4.6.8.9"),
).UnsafeIgnore(),
).Diff2Only(),
),
testgroup("IGNORE_TARGET function CNAME",
tc("Create some records",
@ -2047,9 +1984,6 @@ func makeTests(t *testing.T) []*TestGroup {
),
// https://github.com/StackExchange/dnscontrol/issues/2285
// IGNORE_TARGET for CNAMEs wasn't working for AZURE_DNS.
// Interestingly enough, this has never worked with
// GANDI_V5/diff1. It works on all providers in diff2.
testgroup("IGNORE_TARGET b2285",
tc("Create some records",
cname("foo", "redact1.acm-validations.aws."),
@ -2058,7 +1992,7 @@ func makeTests(t *testing.T) []*TestGroup {
tc("Add a new record - ignoring test.foo.com.",
ignoreTarget("**.acm-validations.aws.", "CNAME"),
).ExpectNoChanges(),
).Diff2Only(),
),
testgroup("structured TXT",
only("OVH"),

View file

@ -119,18 +119,18 @@ func (config *DNSConfig) DomainContainingFQDN(fqdn string) *DomainConfig {
return d
}
// IgnoreName describes an IGNORE_NAME rule.
type IgnoreName struct {
Pattern string `json:"pattern"` // Glob pattern.
Types string `json:"types"` // All caps rtype names, comma separated.
}
// IgnoreTarget describes an IGNORE_TARGET rule.
type IgnoreTarget struct {
Pattern string `json:"pattern"` // Glob pattern.
Type string `json:"type"` // All caps rtype name.
}
func (i *IgnoreTarget) String() string {
return i.Pattern
}
//// IgnoreName describes an IGNORE_NAME rule.
//type IgnoreName struct {
// Pattern string `json:"pattern"` // Glob pattern.
// Types string `json:"types"` // All caps rtype names, comma separated.
//}
//
//// IgnoreTarget describes an IGNORE_TARGET rule.
//type IgnoreTarget struct {
// Pattern string `json:"pattern"` // Glob pattern.
// Type string `json:"type"` // All caps rtype name.
//}
//
//func (i *IgnoreTarget) String() string {
// return i.Pattern
//}

View file

@ -30,8 +30,6 @@ type DomainConfig struct {
EnsureAbsent Records `json:"recordsabsent,omitempty"` // ENSURE_ABSENT
KeepUnknown bool `json:"keepunknown,omitempty"` // NO_PURGE
IgnoredNames []*IgnoreName `json:"ignored_names,omitempty"`
IgnoredTargets []*IgnoreTarget `json:"ignored_targets,omitempty"`
Unmanaged []*UnmanagedConfig `json:"unmanaged,omitempty"` // IGNORE()
UnmanagedUnsafe bool `json:"unmanaged_disable_safety_check,omitempty"` // DISABLE_IGNORE_SAFETY_CHECK

View file

@ -1,14 +1,8 @@
package diff
import (
"fmt"
"regexp"
"sort"
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/printer"
"github.com/fatih/color"
"github.com/gobwas/glob"
)
// Correlation stores a difference between two records.
@ -24,235 +18,18 @@ type Changeset []Correlation
// Differ is an interface for computing the difference between two zones.
type Differ interface {
// IncrementalDiff performs a diff on a record-by-record basis, and returns a sets for which records need to be created, deleted, or modified.
IncrementalDiff(existing []*models.RecordConfig) (unchanged, create, toDelete, modify Changeset, err error)
IncrementalDiff(existing []*models.RecordConfig) (reportMsgs []string, create, toDelete, modify Changeset, err error)
// ChangedGroups performs a diff more appropriate for providers with a "RecordSet" model, where all records with the same name and type are grouped.
// Individual record changes are often not useful in such scenarios. Instead we return a map of record keys to a list of change descriptions within that group.
ChangedGroups(existing []*models.RecordConfig) (map[models.RecordKey][]string, error)
}
// New is a constructor for a Differ.
func New(dc *models.DomainConfig, extraValues ...func(*models.RecordConfig) map[string]string) Differ {
return &differ{
dc: dc,
extraValues: extraValues,
// compile IGNORE_NAME glob patterns
compiledIgnoredNames: compileIgnoredNames(dc.IgnoredNames),
// compile IGNORE_TARGET glob patterns
compiledIgnoredTargets: compileIgnoredTargets(dc.IgnoredTargets),
}
}
// An ignoredName must match both the name glob and one of the recordTypes in rTypes. If rTypes is empty, any
// record type will match.
type ignoredName struct {
nameGlob glob.Glob
rTypes []string
ChangedGroups(existing []*models.RecordConfig) (map[models.RecordKey][]string, []string, error)
}
type differ struct {
dc *models.DomainConfig
extraValues []func(*models.RecordConfig) map[string]string
compiledIgnoredNames []ignoredName
compiledIgnoredTargets []glob.Glob
}
// get normalized content for record. target, ttl, mxprio, and specified metadata
func (d *differ) content(r *models.RecordConfig) string {
// get the extra values maps to add to the comparison.
var allMaps []map[string]string
for _, f := range d.extraValues {
valueMap := f(r)
allMaps = append(allMaps, valueMap)
}
return r.ToDiffable(allMaps...)
}
func apexException(rec *models.RecordConfig) bool {
// Providers often add NS and SOA records at the apex. These
// should not be included in certain checks.
return (rec.Type == "NS" || rec.Type == "SOA") && rec.GetLabel() == "@"
}
func ignoreNameException(rec *models.RecordConfig) bool {
// People wanted it to be possible to disable this safety check.
// Ok, here it is. You now have two risks:
// 1. Two owners (DNSControl and some other entity) toggling a record between two settings.
// 2. The other owner wiping all records at this label, which won't be noticed until the next time dnscontrol is run.
//fmt.Printf("********** DEBUG IGNORE %v %v %q\n", rec.GetLabel(), rec.Type, rec.Metadata["ignore_name_disable_safety_check"])
// See https://github.com/StackExchange/dnscontrol/issues/1106
_, ok := rec.Metadata["ignore_name_disable_safety_check"]
return ok
}
func (d *differ) IncrementalDiff(existing []*models.RecordConfig) (unchanged, create, toDelete, modify Changeset, err error) {
unchanged = Changeset{}
create = Changeset{}
toDelete = Changeset{}
modify = Changeset{}
desired := d.dc.Records
//fmt.Printf("********** DEBUG: STARTING IncrementalDiff\n")
// sort existing and desired by name
existingByNameAndType := map[models.RecordKey][]*models.RecordConfig{}
desiredByNameAndType := map[models.RecordKey][]*models.RecordConfig{}
//fmt.Printf("********** DEBUG: existing list %+v\n", existing)
// Gather the existing records. Skip over any that should be ignored.
for _, e := range existing {
//fmt.Printf("********** DEBUG: existing %v %v %v\n", e.GetLabel(), e.Type, e.GetTargetCombined())
if d.matchIgnoredName(e.GetLabel(), e.Type) {
//fmt.Printf("Ignoring record %s %s due to IGNORE_NAME\n", e.GetLabel(), e.Type)
printer.Debugf("Ignoring record %s %s due to IGNORE_NAME\n", e.GetLabel(), e.Type)
} else if d.matchIgnoredTarget(e.GetTargetField(), e.Type) {
//fmt.Printf("Ignoring record %s %s due to IGNORE_TARGET\n", e.GetLabel(), e.Type)
printer.Debugf("Ignoring record %s %s due to IGNORE_TARGET\n", e.GetLabel(), e.Type)
} else {
k := e.Key()
existingByNameAndType[k] = append(existingByNameAndType[k], e)
}
}
// Review the desired records. If we're modifying one that should be ignored, that's an error.
//fmt.Printf("********** DEBUG: desired list %+v\n", desired)
for _, dr := range desired {
//fmt.Printf("********** DEBUG: desired %v %v %v -- %v %v\n", dr.GetLabel(), dr.Type, dr.GetTargetCombined(), apexException(dr), d.matchIgnoredName(dr.GetLabel()))
if d.matchIgnoredName(dr.GetLabel(), dr.Type) {
//if !apexException(dr) || !ignoreNameException(dr) {
if (!ignoreNameException(dr)) && (!apexException(dr)) {
return nil, nil, nil, nil, fmt.Errorf("trying to update/add IGNORE_NAMEd record: %s %s", dr.GetLabel(), dr.Type)
//} else {
// fmt.Printf("********** DEBUG: desired EXCEPTION\n")
}
} else if d.matchIgnoredTarget(dr.GetTargetField(), dr.Type) {
return nil, nil, nil, nil, fmt.Errorf("trying to update/add IGNORE_TARGETd record: %s %s", dr.GetLabel(), dr.Type)
} else {
k := dr.Key()
desiredByNameAndType[k] = append(desiredByNameAndType[k], dr)
}
}
// if NO_PURGE is set, just remove anything that is only in existing.
if d.dc.KeepUnknown {
for k := range existingByNameAndType {
if _, ok := desiredByNameAndType[k]; !ok {
printer.Debugf("Ignoring record set %s %s due to NO_PURGE\n", k.Type, k.NameFQDN)
delete(existingByNameAndType, k)
}
}
}
// Look through existing records. This will give us changes and deletions and some additions.
// Each iteration is only for a single type/name record set
for key, existingRecords := range existingByNameAndType {
desiredRecords := desiredByNameAndType[key]
// Very first, get rid of any identical records. Easy.
for i := len(existingRecords) - 1; i >= 0; i-- {
ex := existingRecords[i]
for j, de := range desiredRecords {
if d.content(de) != d.content(ex) {
continue
}
unchanged = append(unchanged, Correlation{d, ex, de})
existingRecords = existingRecords[:i+copy(existingRecords[i:], existingRecords[i+1:])]
desiredRecords = desiredRecords[:j+copy(desiredRecords[j:], desiredRecords[j+1:])]
break
}
}
// Next, match by target. This will give the most natural modifications.
for i := len(existingRecords) - 1; i >= 0; i-- {
ex := existingRecords[i]
for j, de := range desiredRecords {
if de.GetTargetField() == ex.GetTargetField() {
// two records share a target, but different content (ttl or metadata changes)
modify = append(modify, Correlation{d, ex, de})
// remove from both slices by index
existingRecords = existingRecords[:i+copy(existingRecords[i:], existingRecords[i+1:])]
desiredRecords = desiredRecords[:j+copy(desiredRecords[j:], desiredRecords[j+1:])]
break
}
}
}
desiredLookup := map[string]*models.RecordConfig{}
existingLookup := map[string]*models.RecordConfig{}
// build index based on normalized content data
for _, ex := range existingRecords {
normalized := d.content(ex)
//fmt.Printf("DEBUG: normalized: %v\n", normalized)
// NB(tlim): Commenting this out. If the provider is returning
// records that are exact duplicates, that's bad and against the
// RFCs. However, we shouldn't error out. Instead, we should
// continue so that we can delete them. Experience shows one
// record will be deleted per iteration but at least the problem
// will fix itself that way. Erroring out means it will require
// manually fixing (going to the control panel, deleting
// individual records, etc.)
//if existingLookup[normalized] != nil {
// return nil, nil, nil, nil, fmt.Errorf("DUPLICATE E_RECORD FOUND: %s %s", key, normalized)
//}
existingLookup[normalized] = ex
}
for _, de := range desiredRecords {
normalized := d.content(de)
if desiredLookup[normalized] != nil {
return nil, nil, nil, nil, fmt.Errorf("DUPLICATE D_RECORD FOUND: %s %s", key, normalized)
}
desiredLookup[normalized] = de
}
// if a record is in both, it is unchanged
for norm, ex := range existingLookup {
if de, ok := desiredLookup[norm]; ok {
unchanged = append(unchanged, Correlation{d, ex, de})
delete(existingLookup, norm)
delete(desiredLookup, norm)
}
}
// sort records by normalized text. Keeps behaviour deterministic
existingStrings, desiredStrings := sortedKeys(existingLookup), sortedKeys(desiredLookup)
// Modifications. Take 1 from each side.
for len(desiredStrings) > 0 && len(existingStrings) > 0 {
modify = append(modify, Correlation{d, existingLookup[existingStrings[0]], desiredLookup[desiredStrings[0]]})
existingStrings = existingStrings[1:]
desiredStrings = desiredStrings[1:]
}
// If desired still has things they are additions
for _, norm := range desiredStrings {
rec := desiredLookup[norm]
create = append(create, Correlation{d, nil, rec})
}
// if found, but not desired, delete it
for _, norm := range existingStrings {
rec := existingLookup[norm]
toDelete = append(toDelete, Correlation{d, rec, nil})
}
// remove this set from the desired list to indicate we have processed it.
delete(desiredByNameAndType, key)
}
// any name/type sets not already processed are pure additions
for name := range existingByNameAndType {
delete(desiredByNameAndType, name)
}
for _, desiredList := range desiredByNameAndType {
for _, rec := range desiredList {
create = append(create, Correlation{d, nil, rec})
}
}
// Sort the lists. This is purely cosmetic.
sort.Slice(unchanged, func(i, j int) bool { return ChangesetLess(unchanged, i, j) })
sort.Slice(create, func(i, j int) bool { return ChangesetLess(create, i, j) })
sort.Slice(toDelete, func(i, j int) bool { return ChangesetLess(toDelete, i, j) })
return
return r.ToDiffable()
}
// ChangesetLess returns true if c[i] < c[j].
@ -291,50 +68,6 @@ func CorrectionLess(c []*models.Correction, i, j int) bool {
return c[i].Msg < c[j].Msg
}
func (d *differ) ChangedGroups(existing []*models.RecordConfig) (map[models.RecordKey][]string, error) {
changedKeys := map[models.RecordKey][]string{}
_, create, toDelete, modify, err := d.IncrementalDiff(existing)
if err != nil {
return nil, err
}
for _, c := range create {
changedKeys[c.Desired.Key()] = append(changedKeys[c.Desired.Key()], c.String())
}
for _, d := range toDelete {
changedKeys[d.Existing.Key()] = append(changedKeys[d.Existing.Key()], d.String())
}
for _, m := range modify {
changedKeys[m.Desired.Key()] = append(changedKeys[m.Desired.Key()], m.String())
}
return changedKeys, nil
}
// DebugKeyMapMap debug prints the results from ChangedGroups.
func DebugKeyMapMap(note string, m map[models.RecordKey][]string) {
// The output isn't pretty but it is useful.
fmt.Println("DEBUG:", note)
// Extract the keys
var keys []models.RecordKey
for k := range m {
keys = append(keys, k)
}
sort.SliceStable(keys, func(i, j int) bool {
if keys[i].NameFQDN == keys[j].NameFQDN {
return keys[i].Type < keys[j].Type
}
return keys[i].NameFQDN < keys[j].NameFQDN
})
// Pretty print the map:
for _, k := range keys {
fmt.Printf(" %v %v:\n", k.Type, k.NameFQDN)
for _, s := range m[k] {
fmt.Printf(" -- %q\n", s)
}
}
}
func (c Correlation) String() string {
if c.Existing == nil {
return color.GreenString("+ CREATE %s %s %s", c.Desired.Type, c.Desired.GetLabelFQDN(), c.d.content(c.Desired))
@ -344,85 +77,3 @@ func (c Correlation) String() string {
}
return color.YellowString("± MODIFY %s %s: (%s) -> (%s)", c.Existing.Type, c.Existing.GetLabelFQDN(), c.d.content(c.Existing), c.d.content(c.Desired))
}
func sortedKeys(m map[string]*models.RecordConfig) []string {
s := []string{}
for v := range m {
s = append(s, v)
}
sort.Strings(s)
return s
}
var spaceCommaTokenizerRegexp = regexp.MustCompile(`\s*,\s*`)
func compileIgnoredNames(ignoredNames []*models.IgnoreName) []ignoredName {
result := make([]ignoredName, 0, len(ignoredNames))
for _, tst := range ignoredNames {
g, err := glob.Compile(tst.Pattern, '.')
if err != nil {
panic(fmt.Sprintf("Failed to compile IGNORE_NAME pattern %q: %v", tst.Pattern, err))
}
t := []string{}
if tst.Types != "" {
t = spaceCommaTokenizerRegexp.Split(tst.Types, -1)
}
result = append(result, ignoredName{nameGlob: g, rTypes: t})
}
return result
}
func compileIgnoredTargets(ignoredTargets []*models.IgnoreTarget) []glob.Glob {
result := make([]glob.Glob, 0, len(ignoredTargets))
for _, tst := range ignoredTargets {
if tst.Type != "CNAME" {
panic(fmt.Sprintf("Invalid rType for IGNORE_TARGET %v", tst.Type))
}
g, err := glob.Compile(tst.Pattern, '.')
if err != nil {
panic(fmt.Sprintf("Failed to compile IGNORE_TARGET pattern %q: %v", tst, err))
}
result = append(result, g)
}
return result
}
func (d *differ) matchIgnoredName(name string, rType string) bool {
for _, tst := range d.compiledIgnoredNames {
//fmt.Printf("********** DEBUG: matchIgnoredName %q %q %v %v\n", name, rType, tst, tst.nameGlob.Match(name))
if tst.nameGlob.Match(name) {
if tst.rTypes == nil {
return true
}
for _, rt := range tst.rTypes {
if rt == "*" || rt == rType {
return true
}
}
}
}
return false
}
func (d *differ) matchIgnoredTarget(target string, rType string) bool {
if rType != "CNAME" {
return false
}
for _, tst := range d.compiledIgnoredTargets {
if tst.Match(target) {
return true
}
}
return false
}

View file

@ -24,10 +24,7 @@ func NewCompat(dc *models.DomainConfig, extraValues ...func(*models.RecordConfig
panic("extraValues not supported")
}
d := New(dc)
return &differCompat{
OldDiffer: d.(*differ),
dc: dc,
}
}
@ -35,40 +32,22 @@ func NewCompat(dc *models.DomainConfig, extraValues ...func(*models.RecordConfig
// differCompat meets the Differ interface but provides its service
// using pkg/diff2 instead of pkg/diff.
type differCompat struct {
OldDiffer *differ // Store the backwards-compatible "d" for pkg/diff
dc *models.DomainConfig
}
// IncrementalDiff generates the diff using the pkg/diff2 code.
// NOTE: While this attempts to be backwards compatible, it does not
// support all features of the old system:
// - The IncrementalDiff() `unchanged` return value is always empty.
// Most providers ignore this return value. If a provider depends on
// that result, please consider one of the pkg/diff2/By*() functions
// instead. (ByZone() is likely to be what you need)
// - The NewCompat() feature `extraValues` is not supported. That
// parameter must be set to nil. If you use that feature, consider
// one of the pkg/diff2/By*() functions.
func (d *differCompat) IncrementalDiff(existing []*models.RecordConfig) (unchanged, toCreate, toDelete, toModify Changeset, err error) {
// IncrementalDiff usees pkg/diff2 to generate output compatible with systems
// still using NewCompat().
func (d *differCompat) IncrementalDiff(existing []*models.RecordConfig) (reportMsgs []string, toCreate, toDelete, toModify Changeset, err error) {
instructions, err := diff2.ByRecord(existing, d.dc, nil)
if err != nil {
return nil, nil, nil, nil, err
}
for _, inst := range instructions {
cor := Correlation{d: d.OldDiffer}
cor := Correlation{}
switch inst.Type {
case diff2.REPORT:
// Sadly the NewCompat function doesn't have an equivalent. We
// just output the messages now.
fmt.Print("INFO: ")
fmt.Println(inst.MsgsJoined)
// TODO(tlim): When diff1 is deleted, IncremtntalDiff should add a
// parameter to list the REPORT messages. It can also eliminate the
// first parameter (existing) since nobody uses that in the diff2
// world.
reportMsgs = append(reportMsgs, inst.Msgs...)
case diff2.CREATE:
cor.Desired = inst.New[0]
toCreate = append(toCreate, cor)
@ -87,12 +66,19 @@ func (d *differCompat) IncrementalDiff(existing []*models.RecordConfig) (unchang
return
}
func GenerateMessageCorrections(msgs []string) (corrections []*models.Correction) {
for _, msg := range msgs {
corrections = append(corrections, &models.Correction{Msg: msg})
}
return
}
// ChangedGroups provides the same results as IncrementalDiff but grouped by key.
func (d *differCompat) ChangedGroups(existing []*models.RecordConfig) (map[models.RecordKey][]string, error) {
func (d *differCompat) ChangedGroups(existing []*models.RecordConfig) (map[models.RecordKey][]string, []string, error) {
changedKeys := map[models.RecordKey][]string{}
_, toCreate, toDelete, toModify, err := d.IncrementalDiff(existing)
toReport, toCreate, toDelete, toModify, err := d.IncrementalDiff(existing)
if err != nil {
return nil, err
return nil, nil, err
}
for _, c := range toCreate {
changedKeys[c.Desired.Key()] = append(changedKeys[c.Desired.Key()], c.String())
@ -103,5 +89,5 @@ func (d *differCompat) ChangedGroups(existing []*models.RecordConfig) (map[model
for _, m := range toModify {
changedKeys[m.Desired.Key()] = append(changedKeys[m.Desired.Key()], m.String())
}
return changedKeys, nil
return changedKeys, toReport, nil
}

View file

@ -1,374 +0,0 @@
package diff
import (
"fmt"
"strconv"
"strings"
"testing"
"github.com/StackExchange/dnscontrol/v4/models"
)
func myRecord(s string) *models.RecordConfig {
parts := strings.Split(s, " ")
ttl, _ := strconv.ParseUint(parts[2], 10, 32)
r := &models.RecordConfig{
Type: parts[1],
TTL: uint32(ttl),
Metadata: map[string]string{},
}
r.SetLabel(parts[0], "example.com")
r.SetTarget(parts[3])
return r
}
func TestAdditionsOnly(t *testing.T) {
desired := []*models.RecordConfig{
myRecord("@ A 1 1.2.3.4"),
}
existing := []*models.RecordConfig{}
checkLengths(t, existing, desired, 0, 1, 0, 0)
}
func TestDeletionsOnly(t *testing.T) {
existing := []*models.RecordConfig{
myRecord("@ A 1 1.2.3.4"),
}
desired := []*models.RecordConfig{}
checkLengths(t, existing, desired, 0, 0, 1, 0)
}
func TestModification(t *testing.T) {
existing := []*models.RecordConfig{
myRecord("www A 1 1.1.1.1"),
myRecord("@ A 1 1.2.3.4"),
}
desired := []*models.RecordConfig{
myRecord("@ A 32 1.2.3.4"),
myRecord("www A 1 1.1.1.1"),
}
un, _, _, mod := checkLengths(t, existing, desired, 1, 0, 0, 1)
if un[0].Desired != desired[1] || un[0].Existing != existing[0] {
t.Error("Expected unchanged records to be correlated")
}
if mod[0].Desired != desired[0] || mod[0].Existing != existing[1] {
t.Errorf("Expected modified records to be correlated")
}
}
func TestUnchangedWithAddition(t *testing.T) {
existing := []*models.RecordConfig{
myRecord("www A 1 1.1.1.1"),
}
desired := []*models.RecordConfig{
myRecord("www A 1 1.2.3.4"),
myRecord("www A 1 1.1.1.1"),
}
un, _, _, _ := checkLengths(t, existing, desired, 1, 1, 0, 0)
if un[0].Desired != desired[1] || un[0].Existing != existing[0] {
t.Errorf("Expected unchanged records to be correlated")
}
}
// s stringifies a RecordConfig for testing purposes.
func s(rc *models.RecordConfig) string {
return fmt.Sprintf("%s %s %d %s", rc.GetLabel(), rc.Type, rc.TTL, rc.GetTargetCombined())
}
func TestOutOfOrderRecords(t *testing.T) {
existing := []*models.RecordConfig{
myRecord("www A 1 1.1.1.1"),
myRecord("www A 1 2.2.2.2"),
myRecord("www A 1 3.3.3.3"),
}
desired := []*models.RecordConfig{
myRecord("www A 1 1.1.1.1"),
myRecord("www A 1 2.2.2.2"),
myRecord("www A 1 2.2.2.3"),
myRecord("www A 10 3.3.3.3"),
}
_, _, _, mods := checkLengths(t, existing, desired, 2, 1, 0, 1)
if s(mods[0].Desired) != s(desired[3]) || s(mods[0].Existing) != s(existing[2]) {
t.Fatalf("Expected to match %s and %s, but matched %s and %s", s(existing[2]), s(desired[3]), s(mods[0].Existing), s(mods[0].Desired))
}
}
func TestMxPrio(t *testing.T) {
existing := []*models.RecordConfig{
myRecord("www MX 1 1.1.1.1"),
}
desired := []*models.RecordConfig{
myRecord("www MX 1 1.1.1.1"),
}
existing[0].MxPreference = 10
desired[0].MxPreference = 20
checkLengths(t, existing, desired, 0, 0, 0, 1)
}
func TestTTLChange(t *testing.T) {
existing := []*models.RecordConfig{
myRecord("www MX 1 1.1.1.1"),
}
desired := []*models.RecordConfig{
myRecord("www MX 10 1.1.1.1"),
}
checkLengths(t, existing, desired, 0, 0, 0, 1)
}
func TestMetaChange(t *testing.T) {
existing := []*models.RecordConfig{
myRecord("www MX 1 1.1.1.1"),
}
desired := []*models.RecordConfig{
myRecord("www MX 1 1.1.1.1"),
}
existing[0].Metadata["k"] = "aa"
desired[0].Metadata["k"] = "bb"
checkLengths(t, existing, desired, 1, 0, 0, 0)
getMeta := func(r *models.RecordConfig) map[string]string {
return map[string]string{
"k": r.Metadata["k"],
}
}
checkLengths(t, existing, desired, 0, 0, 0, 1, getMeta)
}
func TestMetaOrdering(t *testing.T) {
existing := []*models.RecordConfig{
myRecord("www MX 1 1.1.1.1"),
}
desired := []*models.RecordConfig{
myRecord("www MX 1 1.1.1.1"),
}
existing[0].Metadata["k"] = "aa"
existing[0].Metadata["x"] = "cc"
desired[0].Metadata["k"] = "aa"
desired[0].Metadata["x"] = "cc"
checkLengths(t, existing, desired, 1, 0, 0, 0)
getMeta := func(r *models.RecordConfig) map[string]string {
return map[string]string{
"k": r.Metadata["k"],
}
}
checkLengths(t, existing, desired, 1, 0, 0, 0, getMeta)
}
func checkLengths(t *testing.T, existing, desired []*models.RecordConfig, unCount, createCount, delCount, modCount int, valFuncs ...func(*models.RecordConfig) map[string]string) (un, cre, del, mod Changeset) {
return checkLengthsWithKeepUnknown(t, existing, desired, unCount, createCount, delCount, modCount, false, valFuncs...)
}
func checkLengthsWithKeepUnknown(t *testing.T, existing, desired []*models.RecordConfig, unCount, createCount, delCount, modCount int, keepUnknown bool, valFuncs ...func(*models.RecordConfig) map[string]string) (un, cre, del, mod Changeset) {
return checkLengthsFull(t, existing, desired, unCount, createCount, delCount, modCount, keepUnknown, []*models.IgnoreName{}, nil, valFuncs...)
}
func checkLengthsFull(t *testing.T, existing, desired []*models.RecordConfig, unCount, createCount, delCount, modCount int, keepUnknown bool, ignoredRecords []*models.IgnoreName, ignoredTargets []*models.IgnoreTarget, valFuncs ...func(*models.RecordConfig) map[string]string) (un, cre, del, mod Changeset) {
dc := &models.DomainConfig{
Name: "example.com",
Records: desired,
KeepUnknown: keepUnknown,
IgnoredNames: ignoredRecords,
IgnoredTargets: ignoredTargets,
}
d := New(dc, valFuncs...)
un, cre, del, mod, err := d.IncrementalDiff(existing)
if err != nil {
panic(err)
}
if len(un) != unCount {
t.Errorf("Got %d unchanged records, but expected %d", len(un), unCount)
}
if len(cre) != createCount {
t.Errorf("Got %d records to create, but expected %d", len(cre), createCount)
}
if len(del) != delCount {
t.Errorf("Got %d records to delete, but expected %d", len(del), delCount)
}
if len(mod) != modCount {
t.Errorf("Got %d records to modify, but expected %d", len(mod), modCount)
}
if t.Failed() {
t.FailNow()
}
return
}
func TestNoPurge(t *testing.T) {
existing := []*models.RecordConfig{
myRecord("www MX 1 1.1.1.1"),
myRecord("www MX 1 2.2.2.2"),
myRecord("www2 MX 1 1.1.1.1"),
}
desired := []*models.RecordConfig{
myRecord("www MX 1 1.1.1.1"),
}
checkLengthsWithKeepUnknown(t, existing, desired, 1, 0, 1, 0, true)
}
func TestIgnoredRecords(t *testing.T) {
existing := []*models.RecordConfig{
myRecord("www1 A 1 1.1.1.1"),
myRecord("www1 MX 1 1.1.1.1"),
myRecord("www2 A 1 1.1.1.1"),
myRecord("www2 CNAME 1 www"),
myRecord("www2 MX 1 1.1.1.1"),
myRecord("www3 MX 1 1.1.1.1"),
}
desired := []*models.RecordConfig{
myRecord("www3 MX 1 2.2.2.2"),
}
checkLengthsFull(t, existing, desired, 0, 0, 0, 1, false,
[]*models.IgnoreName{
{Pattern: "www1", Types: "*"},
{Pattern: "www2", Types: "A,MX, CNAME"},
},
nil,
)
}
func TestModifyingIgnoredRecords(t *testing.T) {
existing := []*models.RecordConfig{
myRecord("www1 MX 1 1.1.1.1"),
myRecord("www2 MX 1 1.1.1.1"),
myRecord("www3 MX 1 1.1.1.1"),
}
desired := []*models.RecordConfig{
myRecord("www2 MX 1 2.2.2.2"),
}
defer func() {
if r := recover(); r == nil {
t.Errorf("should panic: modification of IGNOREd record")
}
}()
checkLengthsFull(t, existing, desired, 0, 0, 0, 1, false,
[]*models.IgnoreName{{Pattern: "www1", Types: "MX"}, {Pattern: "www2", Types: "*"}},
nil,
)
}
func TestGlobIgnoredName(t *testing.T) {
existing := []*models.RecordConfig{
myRecord("www1 MX 1 1.1.1.1"),
myRecord("foo.www2 MX 1 1.1.1.1"),
myRecord("foo.bar.www3 MX 1 1.1.1.1"),
myRecord("www4 MX 1 1.1.1.1"),
}
desired := []*models.RecordConfig{
myRecord("www4 MX 1 2.2.2.2"),
}
checkLengthsFull(t, existing, desired, 0, 0, 0, 1, false,
[]*models.IgnoreName{
{Pattern: "www1", Types: "*"},
{Pattern: "*.www2", Types: "*"},
{Pattern: "**.www3", Types: "*"},
},
nil,
)
}
func TestInvalidGlobIgnoredName(t *testing.T) {
existing := []*models.RecordConfig{
myRecord("www1 MX 1 1.1.1.1"),
myRecord("www2 MX 1 1.1.1.1"),
myRecord("www3 MX 1 1.1.1.1"),
}
desired := []*models.RecordConfig{
myRecord("www4 MX 1 2.2.2.2"),
}
defer func() {
if r := recover(); r == nil {
t.Errorf("should panic: invalid glob pattern for IGNORE_NAME")
}
}()
checkLengthsFull(t, existing, desired, 0, 1, 0, 0, false,
[]*models.IgnoreName{{Pattern: "www1"}, {Pattern: "*.www2"}, {Pattern: "[.www3"}},
nil,
)
}
func TestGlobIgnoredTarget(t *testing.T) {
existing := []*models.RecordConfig{
myRecord("www1 CNAME 1 ignoreme.com"),
myRecord("foo.www2 MX 1 1.1.1.2"),
myRecord("foo.bar.www3 MX 1 1.1.1.1"),
myRecord("www4 MX 1 1.1.1.1"),
}
desired := []*models.RecordConfig{
myRecord("foo.www2 MX 1 1.1.1.2"),
myRecord("foo.bar.www3 MX 1 1.1.1.1"),
myRecord("www4 MX 1 2.2.2.2"),
}
checkLengthsFull(t, existing, desired, 2, 0, 0, 1, false, nil, []*models.IgnoreTarget{{Pattern: "ignoreme.com", Type: "CNAME"}})
}
func TestInvalidGlobIgnoredTarget(t *testing.T) {
existing := []*models.RecordConfig{
myRecord("www1 MX 1 1.1.1.1"),
myRecord("www2 MX 1 1.1.1.1"),
myRecord("www3 MX 1 1.1.1.1"),
}
desired := []*models.RecordConfig{
myRecord("www4 MX 1 2.2.2.2"),
}
defer func() {
if r := recover(); r == nil {
t.Errorf("should panic: invalid glob pattern for IGNORE_TARGET")
}
}()
checkLengthsFull(t, existing, desired, 0, 1, 0, 0, false, nil, []*models.IgnoreTarget{{Pattern: "[.www3", Type: "CNAME"}})
}
func TestInvalidTypeIgnoredTarget(t *testing.T) {
existing := []*models.RecordConfig{
myRecord("www1 MX 1 1.1.1.1"),
myRecord("www2 MX 1 1.1.1.1"),
myRecord("www3 MX 1 1.1.1.1"),
}
desired := []*models.RecordConfig{
myRecord("www4 MX 1 2.2.2.2"),
}
defer func() {
if r := recover(); r == nil {
t.Errorf("should panic: Invalid rType for IGNORE_TARGET A")
}
}()
checkLengthsFull(t, existing, desired, 0, 1, 0, 0, false, nil, []*models.IgnoreTarget{{Pattern: "1.1.1.1", Type: "A"}})
}
// from https://github.com/StackExchange/dnscontrol/issues/552
func TestCaas(t *testing.T) {
existing := []*models.RecordConfig{
myRecord("test CAA 1 1.1.1.1"),
myRecord("test CAA 1 1.1.1.1"),
myRecord("test CAA 1 1.1.1.1"),
}
desired := []*models.RecordConfig{
myRecord("test CAA 1 1.1.1.1"),
myRecord("test CAA 1 1.1.1.1"),
myRecord("test CAA 1 1.1.1.1"),
}
existing[0].SetTargetCAA(3, "issue", "letsencrypt.org.")
existing[1].SetTargetCAA(3, "issue", "amazon.com.")
existing[2].SetTargetCAA(3, "issuewild", "letsencrypt.org.")
// this will pass or fail depending on the ordering. Not ok.
desired[0].SetTargetCAA(3, "issue", "letsencrypt.org.")
desired[1].SetTargetCAA(3, "issue", "amazon.com.")
desired[2].SetTargetCAA(3, "issuewild", "letsencrypt.org.")
checkLengthsFull(t, existing, desired, 3, 0, 0, 0, false, nil, nil)
// Make sure it passes with a different ordering. Not ok.
desired[2].SetTargetCAA(3, "issue", "letsencrypt.org.")
desired[1].SetTargetCAA(3, "issue", "amazon.com.")
desired[0].SetTargetCAA(3, "issuewild", "letsencrypt.org.")
checkLengthsFull(t, existing, desired, 3, 0, 0, 0, false, nil, nil)
}

View file

@ -1,7 +1,4 @@
package diff2
// EnableDiff2 is true to activate the experimental diff2 algorithm.
var EnableDiff2 bool
// DisableOrdering can be set to true to disable the reordering of the changes
var DisableOrdering bool

View file

@ -831,16 +831,6 @@ function DISABLE_IGNORE_SAFETY_CHECK(d) {
d.unmanaged_disable_safety_check = true;
}
var IGNORE_NAME_DISABLE_SAFETY_CHECK = {
ignore_name_disable_safety_check: 'true',
// (NOTE: diff1 only.)
// This disables a safety check intended to prevent:
// 1. Two owners toggling a record between two settings.
// 2. The other owner wiping all records at this label, which won't
// be noticed until the next time dnscontrol is run.
// See https://github.com/StackExchange/dnscontrol/issues/1106
};
// IGNORE(labelPattern, rtypePattern, targetPattern)
function IGNORE(labelPattern, rtypePattern, targetPattern) {
if (labelPattern === undefined) {
@ -853,9 +843,6 @@ function IGNORE(labelPattern, rtypePattern, targetPattern) {
targetPattern = '*';
}
return function (d) {
// diff1
d.ignored_names.push({ pattern: labelPattern, types: rtypePattern });
// diff2
d.unmanaged.push({
label_pattern: labelPattern,
rType_pattern: rtypePattern,
@ -866,30 +853,11 @@ function IGNORE(labelPattern, rtypePattern, targetPattern) {
// IGNORE_NAME(name, rTypes)
function IGNORE_NAME(name, rTypes) {
if (rTypes === undefined) {
rTypes = '*';
}
return function (d) {
// diff1
d.ignored_names.push({ pattern: name, types: rTypes });
// diff2
d.unmanaged.push({
label_pattern: name,
rType_pattern: rTypes,
});
};
return IGNORE(name, rTypes)
}
function IGNORE_TARGET(target, rType) {
return function (d) {
// diff1
d.ignored_targets.push({ pattern: target, type: rType });
// diff2
d.unmanaged.push({
rType_pattern: rType,
target_pattern: target,
});
};
return IGNORE("*", rType, target)
}
// IMPORT_TRANSFORM(translation_table, domain)

View file

@ -7,60 +7,29 @@
"registrar": "none",
"dnsProviders": {},
"records": [],
"ignored_names": [
{
"pattern": "testignore",
"types": "*"
},
{
"pattern": "testignore2",
"types": "A"
},
{
"pattern": "testignore3",
"types": "A, CNAME, TXT"
},
{
"pattern": "testignore4",
"types": "*"
},
{
"pattern": "legacyignore",
"types": "*"
},
{
"pattern": "@",
"types": "*"
}
],
"ignored_targets": [
{
"pattern": "testtarget",
"type": "CNAME"
},
{
"pattern": "@",
"type": "CNAME"
}
],
"unmanaged": [
{
"label_pattern": "testignore",
"rType_pattern": "*"
"rType_pattern": "*",
"target_pattern": "*"
},
{
"label_pattern": "testignore2",
"rType_pattern": "A"
"rType_pattern": "A",
"target_pattern": "*"
},
{
"label_pattern": "testignore3",
"rType_pattern": "A, CNAME, TXT"
"rType_pattern": "A, CNAME, TXT",
"target_pattern": "*"
},
{
"label_pattern": "testignore4",
"rType_pattern": "*"
"rType_pattern": "*",
"target_pattern": "*"
},
{
"label_pattern": "*",
"rType_pattern": "CNAME",
"target_pattern": "testtarget"
},
@ -71,9 +40,11 @@
},
{
"label_pattern": "@",
"rType_pattern": "*"
"rType_pattern": "*",
"target_pattern": "*"
},
{
"label_pattern": "*",
"rType_pattern": "CNAME",
"target_pattern": "@"
}
@ -84,50 +55,6 @@
"registrar": "none",
"dnsProviders": {},
"records": [],
"ignored_names": [
{
"pattern": "mylabel",
"types": "*"
},
{
"pattern": "mylabel2",
"types": ""
},
{
"pattern": "mylabel3",
"types": ""
},
{
"pattern": "",
"types": "A"
},
{
"pattern": "",
"types": "A,AAAA"
},
{
"pattern": "",
"types": ""
},
{
"pattern": "labelc",
"types": "CNAME"
},
{
"pattern": "nametest",
"types": "*"
}
],
"ignored_targets": [
{
"pattern": "targettest1",
"type": ""
},
{
"pattern": "targettest2",
"type": "A"
}
],
"unmanaged": [
{
"label_pattern": "mylabel",
@ -159,12 +86,16 @@
},
{
"label_pattern": "nametest",
"rType_pattern": "*"
"rType_pattern": "*",
"target_pattern": "*"
},
{
"label_pattern": "*",
"rType_pattern": "*",
"target_pattern": "targettest1"
},
{
"label_pattern": "*",
"rType_pattern": "A",
"target_pattern": "targettest2"
}

View file

@ -7,12 +7,6 @@
"registrar": "none",
"dnsProviders": {},
"records": [],
"ignored_names": [
{
"pattern": "\\*.testignore",
"types": "*"
}
],
"unmanaged": [
{
"label_pattern": "\\*.testignore",
@ -22,4 +16,4 @@
]
}
]
}
}

View file

@ -7,7 +7,6 @@ import (
"strings"
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/pkg/transform"
"github.com/StackExchange/dnscontrol/v4/providers"
"github.com/miekg/dns"
@ -424,11 +423,8 @@ func ValidateAndNormalizeConfig(config *models.DNSConfig) (errs []error) {
// Populate FQDN:
rec.SetLabel(rec.GetLabel(), domain.Name)
// Warn if a diff1-only feature is used in diff2 mode.
if diff2.EnableDiff2 {
if _, ok := rec.Metadata["ignore_name_disable_safety_check"]; ok {
errs = append(errs, fmt.Errorf("IGNORE_NAME_DISABLE_SAFETY_CHECK no longer supported. Please use DISABLE_IGNORE_SAFETY_CHECK for the entire domain"))
}
if _, ok := rec.Metadata["ignore_name_disable_safety_check"]; ok {
errs = append(errs, fmt.Errorf("IGNORE_NAME_DISABLE_SAFETY_CHECK no longer supported. Please use DISABLE_IGNORE_SAFETY_CHECK for the entire domain"))
}
}

View file

@ -92,9 +92,7 @@ func (c ConsolePrinter) PrintCorrection(i int, correction *models.Correction) {
// PrintReport is called to print/format each non-mutating correction (diff2.REPORT).
func (c ConsolePrinter) PrintReport(i int, correction *models.Correction) {
// When diff1 is eliminated:
//fmt.Fprintf(c.Writer, "INFO#%d: %s\n", i+1, correction.Msg)
fmt.Fprintf(c.Writer, "INFO: %s\n", correction.Msg)
fmt.Fprintf(c.Writer, "INFO#%d: %s\n", i+1, correction.Msg)
}
// PromptToRun prompts the user to see if they want to execute a correction.

View file

@ -16,7 +16,6 @@ import (
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/pkg/printer"
"github.com/StackExchange/dnscontrol/v4/pkg/txtutil"
"github.com/StackExchange/dnscontrol/v4/providers"
@ -109,17 +108,12 @@ func (a *edgeDNSProvider) EnsureZoneExists(domain string) error {
func (a *edgeDNSProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, error) {
txtutil.SplitSingleLongTxt(existingRecords)
var corrections []*models.Correction
var keysToUpdate map[models.RecordKey][]string
var err error
if !diff2.EnableDiff2 {
keysToUpdate, err = (diff.New(dc)).ChangedGroups(existingRecords)
} else {
keysToUpdate, err = (diff.NewCompat(dc)).ChangedGroups(existingRecords)
}
keysToUpdate, toReport, err := diff.NewCompat(dc).ChangedGroups(existingRecords)
if err != nil {
return nil, err
}
// Start corrections with the reports
corrections := diff.GenerateMessageCorrections(toReport)
existingRecordsMap := make(map[models.RecordKey][]*models.RecordConfig)
for _, r := range existingRecords {

View file

@ -10,9 +10,7 @@ import (
"strings"
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/pkg/printer"
"github.com/StackExchange/dnscontrol/v4/pkg/txtutil"
"github.com/StackExchange/dnscontrol/v4/providers"
)
@ -72,103 +70,7 @@ func (api *autoDNSProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, e
domain := dc.Name
txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records
var changes []*models.RecordConfig
var corrections []*models.Correction
if !diff2.EnableDiff2 {
differ := diff.New(dc)
unchanged, create, del, modify, err := differ.IncrementalDiff(existingRecords)
if err != nil {
return nil, err
}
for _, m := range unchanged {
changes = append(changes, m.Desired)
}
for _, m := range del {
// Just notify, these records don't have to be deleted explicitly
printer.Debugf(m.String())
}
for _, m := range create {
printer.Debugf(m.String())
changes = append(changes, m.Desired)
}
for _, m := range modify {
printer.Debugf("mod")
printer.Debugf(m.String())
changes = append(changes, m.Desired)
}
if len(create) > 0 || len(del) > 0 || len(modify) > 0 {
corrections = append(corrections,
&models.Correction{
Msg: "Zone update for " + domain,
F: func() error {
zoneTTL := uint32(0)
nameServers := []*models.Nameserver{}
resourceRecords := []*ResourceRecord{}
for _, record := range changes {
// NS records for the APEX should be handled differently
if record.Type == "NS" && record.Name == "@" {
nameServers = append(nameServers, &models.Nameserver{
Name: strings.TrimSuffix(record.GetTargetField(), "."),
})
zoneTTL = record.TTL
} else {
resourceRecord := &ResourceRecord{
Name: record.Name,
TTL: int64(record.TTL),
Type: record.Type,
Value: record.GetTargetField(),
}
if resourceRecord.Name == "@" {
resourceRecord.Name = ""
}
if record.Type == "MX" {
resourceRecord.Pref = int32(record.MxPreference)
}
if record.Type == "SRV" {
resourceRecord.Value = fmt.Sprintf(
"%d %d %d %s",
record.SrvPriority,
record.SrvWeight,
record.SrvPort,
record.GetTargetField(),
)
}
if record.Type == "CAA" {
resourceRecord.Value = fmt.Sprintf("%d %s \"%s\"",
record.CaaFlag,
record.CaaTag,
record.GetTargetField(),
)
}
resourceRecords = append(resourceRecords, resourceRecord)
}
}
err := api.updateZone(domain, resourceRecords, nameServers, zoneTTL)
if err != nil {
return fmt.Errorf(err.Error())
}
return nil
},
})
}
return corrections, nil
}
msgs, changed, err := diff2.ByZone(existingRecords, dc, nil)
if err != nil {

View file

@ -12,7 +12,6 @@ axfrddns -
*/
import (
"bytes"
"crypto/tls"
"encoding/base64"
"encoding/json"
@ -24,7 +23,6 @@ import (
"time"
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/pkg/printer"
"github.com/StackExchange/dnscontrol/v4/pkg/txtutil"
@ -460,108 +458,6 @@ func (c *axfrddnsProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, fo
return nil, err
}
if !diff2.EnableDiff2 {
// Legacy code with the old `diff`
differ := diff.New(dc)
_, create, del, mod, err := differ.IncrementalDiff(foundRecords)
if err != nil {
return nil, err
}
changes := false
buf := &bytes.Buffer{}
buf2 := &bytes.Buffer{}
// See comment below about hasNSDeletion.
hasNSDeletion := false
for _, c := range create {
if c.Desired.Type == "NS" && c.Desired.Name == "@" {
hasNSDeletion = true
continue
}
}
for _, c := range del {
if c.Existing.Type == "NS" && c.Existing.Name == "@" {
hasNSDeletion = true
continue
}
}
for _, c := range mod {
if c.Existing.Type == "NS" && c.Existing.Name == "@" {
hasNSDeletion = true
continue
}
}
if hasNSDeletion {
update.Insert([]dns.RR{dummyNs1})
}
for _, change := range del {
changes = true
fmt.Fprintln(buf, change)
update.Remove([]dns.RR{change.Existing.ToRR()})
}
for _, change := range mod {
changes = true
if c.serverHasBuggyCNAME && change.Desired.Type == "CNAME" {
fmt.Fprintln(buf, change.String()+color.RedString(" (delete)"))
update.Remove([]dns.RR{change.Existing.ToRR()})
hasTwoCorrections = true
fmt.Fprintln(buf2, change.String()+color.GreenString(" (create)"))
update2.Insert([]dns.RR{change.Desired.ToRR()})
} else {
fmt.Fprintln(buf, change)
update.Remove([]dns.RR{change.Existing.ToRR()})
update.Insert([]dns.RR{change.Desired.ToRR()})
}
}
for _, change := range create {
changes = true
splitted := false
if c.serverHasBuggyCNAME && change.Desired.Type == "CNAME" {
for _, change2 := range del {
if change2.Existing.Name == change.Desired.Name {
splitted = true
break
}
}
}
if splitted {
hasTwoCorrections = true
fmt.Fprintln(buf2, change)
update2.Insert([]dns.RR{change.Desired.ToRR()})
} else {
fmt.Fprintln(buf, change)
update.Insert([]dns.RR{change.Desired.ToRR()})
}
}
if hasNSDeletion {
update.Remove([]dns.RR{dummyNs2})
}
if !changes {
return nil, nil
}
if hasTwoCorrections {
return []*models.Correction{
c.BuildCorrection(dc, []string{buf.String()}, update),
c.BuildCorrection(dc, []string{buf2.String()}, update2),
}, nil
}
return []*models.Correction{
c.BuildCorrection(dc, []string{buf.String()}, update),
}, nil
}
changes, err := diff2.ByRecord(foundRecords, dc, nil)
if err != nil {
return nil, err

View file

@ -4,7 +4,6 @@ import (
"context"
"encoding/json"
"fmt"
"sort"
"strings"
"time"
@ -12,7 +11,6 @@ import (
adns "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns"
"github.com/Azure/go-autorest/autorest/to"
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/pkg/printer"
"github.com/StackExchange/dnscontrol/v4/pkg/txtutil"
@ -197,145 +195,8 @@ func (a *azurednsProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, ex
txtutil.SplitSingleLongTxt(existingRecords) // Autosplit long TXT records
var corrections []*models.Correction
if !diff2.EnableDiff2 {
records := a.rawRecords[dc.Name]
zoneName := a.zoneName[dc.Name]
differ := diff.New(dc)
namesToUpdate, err := differ.ChangedGroups(existingRecords)
if err != nil {
return nil, err
}
if len(namesToUpdate) == 0 {
return nil, nil
}
updates := map[models.RecordKey][]*models.RecordConfig{}
for k := range namesToUpdate {
updates[k] = nil
for _, rc := range dc.Records {
if rc.Key() == k {
updates[k] = append(updates[k], rc)
}
}
}
for k, recs := range updates {
if len(recs) == 0 {
var rrset *adns.RecordSet
for _, r := range records {
if strings.TrimSuffix(*r.Properties.Fqdn, ".") == k.NameFQDN {
n1, err := nativeToRecordType(r.Type)
if err != nil {
return nil, err
}
n2, err := nativeToRecordType(to.StringPtr(k.Type))
if err != nil {
return nil, err
}
if n1 == n2 {
rrset = r
break
}
}
}
if rrset != nil {
corrections = append(corrections,
&models.Correction{
Msg: strings.Join(namesToUpdate[k], "\n"),
F: func() error {
ctx, cancel := context.WithTimeout(context.Background(), 6000*time.Second)
defer cancel()
rt, err := nativeToRecordType(rrset.Type)
if err != nil {
return err
}
//fmt.Fprintf(os.Stderr, "DEBUG: 1 a.recordsClient.Delete(ctx, %v, %v, %v, %v)\n", *a.resourceGroup, zoneName, *rrset.Name, rt)
_, err = a.recordsClient.Delete(ctx, *a.resourceGroup, zoneName, *rrset.Name, rt, nil)
if err != nil {
return err
}
return nil
},
})
} else {
return nil, fmt.Errorf("no record set found to delete. Name: '%s'. Type: '%s'", k.NameFQDN, k.Type)
}
} else {
rrset, recordType, err := a.recordToNative(k, recs)
if err != nil {
return nil, err
}
var recordName string
for _, r := range recs {
i := int64(r.TTL)
rrset.Properties.TTL = &i // TODO: make sure that ttls are consistent within a set
recordName = r.Name
}
for _, r := range records {
existingRecordType, err := nativeToRecordType(r.Type)
if err != nil {
return nil, err
}
changedRecordType, err := nativeToRecordType(to.StringPtr(k.Type))
if err != nil {
return nil, err
}
if strings.TrimSuffix(*r.Properties.Fqdn, ".") == k.NameFQDN && (changedRecordType == adns.RecordTypeCNAME || existingRecordType == adns.RecordTypeCNAME) {
if existingRecordType == adns.RecordTypeA || existingRecordType == adns.RecordTypeAAAA || changedRecordType == adns.RecordTypeA || changedRecordType == adns.RecordTypeAAAA { //CNAME cannot coexist with an A or AA
corrections = append(corrections,
&models.Correction{
Msg: strings.Join(namesToUpdate[k], "\n"),
F: func() error {
ctx, cancel := context.WithTimeout(context.Background(), 6000*time.Second)
defer cancel()
//fmt.Fprintf(os.Stderr, "DEBUG: 2 a.recordsClient.Delete(ctx, %v, %v, %v, %v, nil)\n", *a.resourceGroup, zoneName, recordName, existingRecordType)
_, err := a.recordsClient.Delete(ctx, *a.resourceGroup, zoneName, recordName, existingRecordType, nil)
if err != nil {
return err
}
return nil
},
})
}
}
}
corrections = append(corrections,
&models.Correction{
Msg: strings.Join(namesToUpdate[k], "\n"),
F: func() error {
ctx, cancel := context.WithTimeout(context.Background(), 6000*time.Second)
defer cancel()
_, err := a.recordsClient.CreateOrUpdate(ctx, *a.resourceGroup, zoneName, recordName, recordType, *rrset, nil)
if err != nil {
return err
}
return nil
},
})
}
}
// Sort the records for cosmetic reasons: It just makes a long list
// of deletes or adds easier to read if they are in sorted order.
// That said, it may be risky to sort them (sort key is the text
// message "Msg") if there are deletes that must happen before adds.
// Reading the above code it isn't clear that any of the updates are
// order-dependent. That said, all the tests pass.
// If in the future this causes a bug, we can either just remove
// this next line, or (even better) put any order-dependent
// operations in a single models.Correction{}.
sort.Slice(corrections, func(i, j int) bool { return diff.CorrectionLess(corrections, i, j) })
return corrections, nil
}
// Azure is a "ByRSet" API.
// Azure is a "ByRecordSet" API.
changes, err := diff2.ByRecordSet(existingRecords, dc, nil)
if err != nil {
@ -415,35 +276,6 @@ func (a *azurednsProvider) recordDelete(zoneName string, reckey models.RecordKey
return err
}
func nativeToRecordType(recordType *string) (adns.RecordType, error) {
recordTypeStripped := strings.TrimPrefix(*recordType, "Microsoft.Network/dnszones/")
switch recordTypeStripped {
case "A", "AZURE_ALIAS_A":
return adns.RecordTypeA, nil
case "AAAA", "AZURE_ALIAS_AAAA":
return adns.RecordTypeAAAA, nil
case "CAA":
return adns.RecordTypeCAA, nil
case "CNAME", "AZURE_ALIAS_CNAME":
return adns.RecordTypeCNAME, nil
case "MX":
return adns.RecordTypeMX, nil
case "NS":
return adns.RecordTypeNS, nil
case "PTR":
return adns.RecordTypePTR, nil
case "SRV":
return adns.RecordTypeSRV, nil
case "TXT":
return adns.RecordTypeTXT, nil
case "SOA":
return adns.RecordTypeSOA, nil
default:
// Unimplemented type. Return adns.A as a decoy, but send an error.
return adns.RecordTypeA, fmt.Errorf("nativeToRecordType rtype %v unimplemented", *recordType)
}
}
func nativeToRecordTypeDiff2(recordType *string) (adns.RecordType, error) {
recordTypeStripped := strings.TrimPrefix(*recordType, "Microsoft.Network/dnszones/")
switch recordTypeStripped {
@ -622,74 +454,6 @@ func nativeToRecords(set *adns.RecordSet, origin string) []*models.RecordConfig
return results
}
func (a *azurednsProvider) recordToNative(recordKey models.RecordKey, recordConfig []*models.RecordConfig) (*adns.RecordSet, adns.RecordType, error) {
recordSet := &adns.RecordSet{Type: to.StringPtr(recordKey.Type), Properties: &adns.RecordSetProperties{}}
for _, rec := range recordConfig {
switch recordKey.Type {
case "A":
if recordSet.Properties.ARecords == nil {
recordSet.Properties.ARecords = []*adns.ARecord{}
}
recordSet.Properties.ARecords = append(recordSet.Properties.ARecords, &adns.ARecord{IPv4Address: to.StringPtr(rec.GetTargetField())})
case "AAAA":
if recordSet.Properties.AaaaRecords == nil {
recordSet.Properties.AaaaRecords = []*adns.AaaaRecord{}
}
recordSet.Properties.AaaaRecords = append(recordSet.Properties.AaaaRecords, &adns.AaaaRecord{IPv6Address: to.StringPtr(rec.GetTargetField())})
case "CNAME":
recordSet.Properties.CnameRecord = &adns.CnameRecord{Cname: to.StringPtr(rec.GetTargetField())}
case "NS":
if recordSet.Properties.NsRecords == nil {
recordSet.Properties.NsRecords = []*adns.NsRecord{}
}
recordSet.Properties.NsRecords = append(recordSet.Properties.NsRecords, &adns.NsRecord{Nsdname: to.StringPtr(rec.GetTargetField())})
case "PTR":
if recordSet.Properties.PtrRecords == nil {
recordSet.Properties.PtrRecords = []*adns.PtrRecord{}
}
recordSet.Properties.PtrRecords = append(recordSet.Properties.PtrRecords, &adns.PtrRecord{Ptrdname: to.StringPtr(rec.GetTargetField())})
case "TXT":
if recordSet.Properties.TxtRecords == nil {
recordSet.Properties.TxtRecords = []*adns.TxtRecord{}
}
// Empty TXT record needs to have no value set in it's properties
if !(len(rec.TxtStrings) == 1 && rec.TxtStrings[0] == "") {
var txts []*string
for _, txt := range rec.TxtStrings {
txts = append(txts, to.StringPtr(txt))
}
recordSet.Properties.TxtRecords = append(recordSet.Properties.TxtRecords, &adns.TxtRecord{Value: txts})
}
case "MX":
if recordSet.Properties.MxRecords == nil {
recordSet.Properties.MxRecords = []*adns.MxRecord{}
}
recordSet.Properties.MxRecords = append(recordSet.Properties.MxRecords, &adns.MxRecord{Exchange: to.StringPtr(rec.GetTargetField()), Preference: to.Int32Ptr(int32(rec.MxPreference))})
case "SRV":
if recordSet.Properties.SrvRecords == nil {
recordSet.Properties.SrvRecords = []*adns.SrvRecord{}
}
recordSet.Properties.SrvRecords = append(recordSet.Properties.SrvRecords, &adns.SrvRecord{Target: to.StringPtr(rec.GetTargetField()), Port: to.Int32Ptr(int32(rec.SrvPort)), Weight: to.Int32Ptr(int32(rec.SrvWeight)), Priority: to.Int32Ptr(int32(rec.SrvPriority))})
case "CAA":
if recordSet.Properties.CaaRecords == nil {
recordSet.Properties.CaaRecords = []*adns.CaaRecord{}
}
recordSet.Properties.CaaRecords = append(recordSet.Properties.CaaRecords, &adns.CaaRecord{Value: to.StringPtr(rec.GetTargetField()), Tag: to.StringPtr(rec.CaaTag), Flags: to.Int32Ptr(int32(rec.CaaFlag))})
case "AZURE_ALIAS_A", "AZURE_ALIAS_AAAA", "AZURE_ALIAS_CNAME":
*recordSet.Type = rec.AzureAlias["type"]
recordSet.Properties.TargetResource = &adns.SubResource{ID: to.StringPtr(rec.GetTargetField())}
default:
return nil, adns.RecordTypeA, fmt.Errorf("recordToNative rtype %v unimplemented", recordKey.Type) // ands.A is a placeholder
}
}
rt, err := nativeToRecordType(to.StringPtr(*recordSet.Type))
if err != nil {
return nil, adns.RecordTypeA, err // adns.A is a placeholder
}
return recordSet, rt, nil
}
// NOTE recordToNativeDiff2 is really "convert []RecordConfig to rrset".
func (a *azurednsProvider) recordToNativeDiff2(recordKey models.RecordKey, recordConfig []*models.RecordConfig) (*adns.RecordSet, adns.RecordType, error) {

View file

@ -14,7 +14,6 @@ bind -
*/
import (
"bytes"
"encoding/json"
"fmt"
"os"
@ -23,7 +22,6 @@ import (
"time"
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/pkg/prettyzone"
"github.com/StackExchange/dnscontrol/v4/pkg/printer"
@ -213,6 +211,7 @@ func ParseZoneContents(content string, zoneName string, zonefileName string) (mo
// GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records.
func (c *bindProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, foundRecords models.Records) ([]*models.Correction, error) {
txtutil.SplitSingleLongTxt(dc.Records)
var corrections []*models.Correction
changes := false
var msg string
@ -241,111 +240,68 @@ func (c *bindProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, foundR
c.skipNextSoaIncrease = true
}
if !diff2.EnableDiff2 {
differ := diff.New(dc)
_, create, del, mod, err := differ.IncrementalDiff(foundRecords)
if err != nil {
return nil, err
}
buf := &bytes.Buffer{}
// Print a list of changes. Generate an actual change that is the zone
for _, i := range create {
changes = true
if c.zoneFileFound {
fmt.Fprintln(buf, i)
}
}
for _, i := range del {
changes = true
if c.zoneFileFound {
fmt.Fprintln(buf, i)
}
}
for _, i := range mod {
changes = true
if c.zoneFileFound {
fmt.Fprintln(buf, i)
}
}
if c.zoneFileFound {
msg = fmt.Sprintf("GENERATE_ZONEFILE: '%s'. Changes:\n%s", dc.Name, buf)
} else {
msg = fmt.Sprintf("GENERATE_ZONEFILE: '%s' (new file with %d records)\n", dc.Name, len(create))
}
} else {
var msgs []string
var err error
msgs, changes, err = diff2.ByZone(foundRecords, dc, nil)
if err != nil {
return nil, err
}
//fmt.Printf("DEBUG: BIND changes=%v\n", changes)
msg = strings.Join(msgs, "\n")
var msgs []string
var err error
msgs, changes, err = diff2.ByZone(foundRecords, dc, nil)
if err != nil {
return nil, err
}
if !changes {
return nil, nil
}
msg = strings.Join(msgs, "\n")
comments := make([]string, 0, 5)
comments = append(comments,
fmt.Sprintf("generated with dnscontrol %s", time.Now().Format(time.RFC3339)),
)
if dc.AutoDNSSEC == "on" {
// This does nothing but reminds the user to add the correct
// auto-dnssecc zone statement to named.conf.
// While it is a no-op, it is useful for situations where a zone
// has multiple providers.
comments = append(comments, "Automatic DNSSEC signing requested")
}
var corrections []*models.Correction
//fmt.Printf("DEBUG: BIND changes=%v\n", changes)
if changes {
c.zonefile = filepath.Join(c.directory,
makeFileName(c.filenameformat,
dc.Metadata[models.DomainUniqueName], dc.Name, dc.Metadata[models.DomainTag]),
)
comments := make([]string, 0, 5)
comments = append(comments,
fmt.Sprintf("generated with dnscontrol %s", time.Now().Format(time.RFC3339)),
)
if dc.AutoDNSSEC == "on" {
// This does nothing but reminds the user to add the correct
// auto-dnssecc zone statement to named.conf.
// While it is a no-op, it is useful for situations where a zone
// has multiple providers.
comments = append(comments, "Automatic DNSSEC signing requested")
}
c.zonefile = filepath.Join(c.directory,
makeFileName(c.filenameformat,
dc.Metadata[models.DomainUniqueName], dc.Name, dc.Metadata[models.DomainTag]),
)
// We only change the serial number if there is a change.
if !c.skipNextSoaIncrease {
desiredSoa.SoaSerial = nextSerial
}
corrections = append(corrections,
&models.Correction{
Msg: msg,
F: func() error {
printer.Printf("WRITING ZONEFILE: %v\n", c.zonefile)
fname, err := preprocessFilename(c.zonefile)
if err != nil {
return fmt.Errorf("could not create zonefile: %w", err)
}
zf, err := os.Create(fname)
if err != nil {
return fmt.Errorf("could not create zonefile: %w", err)
}
// Beware that if there are any fake types, then they will
// be commented out on write, but we don't reverse that when
// reading, so there will be a diff on every invocation.
err = prettyzone.WriteZoneFileRC(zf, dc.Records, dc.Name, 0, comments)
if err != nil {
return fmt.Errorf("failed WriteZoneFile: %w", err)
}
err = zf.Close()
if err != nil {
return fmt.Errorf("closing: %w", err)
}
return nil
},
})
// We only change the serial number if there is a change.
if !c.skipNextSoaIncrease {
desiredSoa.SoaSerial = nextSerial
}
corrections = append(corrections,
&models.Correction{
Msg: msg,
F: func() error {
printer.Printf("WRITING ZONEFILE: %v\n", c.zonefile)
fname, err := preprocessFilename(c.zonefile)
if err != nil {
return fmt.Errorf("could not create zonefile: %w", err)
}
zf, err := os.Create(fname)
if err != nil {
return fmt.Errorf("could not create zonefile: %w", err)
}
// Beware that if there are any fake types, then they will
// be commented out on write, but we don't reverse that when
// reading, so there will be a diff on every invocation.
err = prettyzone.WriteZoneFileRC(zf, dc.Records, dc.Name, 0, comments)
if err != nil {
return fmt.Errorf("failed WriteZoneFile: %w", err)
}
err = zf.Close()
if err != nil {
return fmt.Errorf("closing: %w", err)
}
return nil
},
})
return corrections, nil
}

View file

@ -11,7 +11,6 @@ import (
"golang.org/x/net/idna"
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/pkg/printer"
"github.com/StackExchange/dnscontrol/v4/pkg/transform"
@ -227,106 +226,6 @@ func (c *cloudflareProvider) GetZoneRecordsCorrections(dc *models.DomainConfig,
checkNSModifications(dc)
var corrections []*models.Correction
if !diff2.EnableDiff2 {
differ := diff.New(dc, getProxyMetadata)
_, create, del, mod, err := differ.IncrementalDiff(records)
if err != nil {
return nil, err
}
corrections := []*models.Correction{}
for _, d := range del {
ex := d.Existing
if ex.Type == "PAGE_RULE" {
corrections = append(corrections, &models.Correction{
Msg: d.String(),
F: func() error { return c.deletePageRule(ex.Original.(cloudflare.PageRule).ID, domainID) },
})
} else if ex.Type == "WORKER_ROUTE" {
corrections = append(corrections, &models.Correction{
Msg: d.String(),
F: func() error { return c.deleteWorkerRoute(ex.Original.(cloudflare.WorkerRoute).ID, domainID) },
})
} else {
corr := c.deleteRec(ex.Original.(cloudflare.DNSRecord), domainID)
// DS records must always have a corresponding NS record.
// Therefore, we remove DS records before any NS records.
if d.Existing.Type == "DS" {
corrections = append([]*models.Correction{corr}, corrections...)
} else {
corrections = append(corrections, corr)
}
}
}
for _, d := range create {
des := d.Desired
if des.Type == "PAGE_RULE" {
corrections = append(corrections, &models.Correction{
Msg: d.String(),
F: func() error { return c.createPageRule(domainID, des.GetTargetField()) },
})
} else if des.Type == "WORKER_ROUTE" {
corrections = append(corrections, &models.Correction{
Msg: d.String(),
F: func() error { return c.createWorkerRoute(domainID, des.GetTargetField()) },
})
} else {
corr := c.createRec(des, domainID)
// DS records must always have a corresponding NS record.
// Therefore, we create NS records before any DS records.
if d.Desired.Type == "NS" {
corrections = append(corr, corrections...)
} else {
corrections = append(corrections, corr...)
}
}
}
for _, d := range mod {
rec := d.Desired
ex := d.Existing
if rec.Type == "PAGE_RULE" {
corrections = append(corrections, &models.Correction{
Msg: d.String(),
F: func() error {
return c.updatePageRule(ex.Original.(cloudflare.PageRule).ID, domainID, rec.GetTargetField())
},
})
} else if rec.Type == "WORKER_ROUTE" {
corrections = append(corrections, &models.Correction{
Msg: d.String(),
F: func() error {
return c.updateWorkerRoute(ex.Original.(cloudflare.WorkerRoute).ID, domainID, rec.GetTargetField())
},
})
} else {
e := ex.Original.(cloudflare.DNSRecord)
proxy := e.Proxiable && rec.Metadata[metaProxy] != "off"
corrections = append(corrections, &models.Correction{
Msg: d.String(),
F: func() error { return c.modifyRecord(domainID, e.ID, proxy, rec) },
})
}
}
// Add universalSSL change to corrections when needed
if changed, newState, err := c.checkUniversalSSL(dc, domainID); err == nil && changed {
var newStateString string
if newState {
newStateString = "enabled"
} else {
newStateString = "disabled"
}
corrections = append(corrections, &models.Correction{
Msg: fmt.Sprintf("Universal SSL will be %s for this domain.", newStateString),
F: func() error { return c.changeUniversalSSL(domainID, newState) },
})
}
return corrections, nil
}
// Cloudflare is a "ByRecord" API.
instructions, err := diff2.ByRecord(records, dc, genComparable)

View file

@ -54,17 +54,6 @@ func (c *cloudflareProvider) deleteDNSRecord(rec cloudflare.DNSRecord, domainID
return c.cfClient.DeleteDNSRecord(context.Background(), cloudflare.ZoneIdentifier(domainID), rec.ID)
}
// create a correction to delete a record
func (c *cloudflareProvider) deleteRec(rec cloudflare.DNSRecord, domainID string) *models.Correction {
return &models.Correction{
Msg: fmt.Sprintf("DELETE record: %s %s %d %q (id=%s)", rec.Name, rec.Type, rec.TTL, rec.Content, rec.ID),
F: func() error {
err := c.cfClient.DeleteDNSRecord(context.Background(), cloudflare.ZoneIdentifier(domainID), rec.ID)
return err
},
}
}
func (c *cloudflareProvider) createZone(domainName string) (string, error) {
zone, err := c.cfClient.CreateZone(context.Background(), domainName, false, cloudflare.Account{ID: c.accountID}, "full")
return zone.ID, err
@ -129,69 +118,6 @@ func cfNaptrData(rec *models.RecordConfig) *cfNaptrRecData {
}
}
func (c *cloudflareProvider) createRec(rec *models.RecordConfig, domainID string) []*models.Correction {
var id string
content := rec.GetTargetField()
if rec.Metadata[metaOriginalIP] != "" {
content = rec.Metadata[metaOriginalIP]
}
prio := ""
if rec.Type == "MX" {
prio = fmt.Sprintf(" %d ", rec.MxPreference)
}
if rec.Type == "TXT" {
content = rec.GetTargetTXTJoined()
}
if rec.Type == "DS" {
content = fmt.Sprintf("%d %d %d %s", rec.DsKeyTag, rec.DsAlgorithm, rec.DsDigestType, rec.DsDigest)
}
arr := []*models.Correction{{
Msg: fmt.Sprintf("CREATE record: %s %s %d%s %s", rec.GetLabel(), rec.Type, rec.TTL, prio, content),
F: func() error {
cf := cloudflare.CreateDNSRecordParams{
Name: rec.GetLabel(),
Type: rec.Type,
TTL: int(rec.TTL),
Content: content,
Priority: &rec.MxPreference,
}
if rec.Type == "SRV" {
cf.Data = cfSrvData(rec)
cf.Name = rec.GetLabelFQDN()
} else if rec.Type == "CAA" {
cf.Data = cfCaaData(rec)
cf.Name = rec.GetLabelFQDN()
cf.Content = ""
} else if rec.Type == "TLSA" {
cf.Data = cfTlsaData(rec)
cf.Name = rec.GetLabelFQDN()
} else if rec.Type == "SSHFP" {
cf.Data = cfSshfpData(rec)
cf.Name = rec.GetLabelFQDN()
} else if rec.Type == "DS" {
cf.Data = cfDSData(rec)
} else if rec.Type == "NAPTR" {
cf.Data = cfNaptrData(rec)
cf.Name = rec.GetLabelFQDN()
}
resp, err := c.cfClient.CreateDNSRecord(context.Background(), cloudflare.ZoneIdentifier(domainID), cf)
if err != nil {
return err
}
// Updating id (from the outer scope) by side-effect, required for updating proxy mode
id = resp.ID
return nil
},
}}
if rec.Metadata[metaProxy] != "off" {
arr = append(arr, &models.Correction{
Msg: fmt.Sprintf("ACTIVATE PROXY for new record %s %s %d %s", rec.GetLabel(), rec.Type, rec.TTL, rec.GetTargetField()),
F: func() error { return c.modifyRecord(domainID, id, true, rec) },
})
}
return arr
}
func (c *cloudflareProvider) createRecDiff2(rec *models.RecordConfig, domainID string, msg string) []*models.Correction {
content := rec.GetTargetField()

View file

@ -8,7 +8,6 @@ import (
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/providers"
"github.com/miekg/dns/dnsutil"
)
@ -129,17 +128,12 @@ func (c *cloudnsProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, exi
record.TTL = fixTTL(record.TTL)
}
var corrections []*models.Correction
var differ diff.Differ
if !diff2.EnableDiff2 {
differ = diff.New(dc)
} else {
differ = diff.NewCompat(dc)
}
_, create, del, modify, err := differ.IncrementalDiff(existingRecords)
toReport, create, del, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords)
if err != nil {
return nil, err
}
// Start corrections with the reports
corrections := diff.GenerateMessageCorrections(toReport)
// Deletes first so changing type works etc.
for _, m := range del {

View file

@ -5,7 +5,6 @@ import (
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
)
// GetZoneRecords gets the records of a zone and returns them in RecordConfig format.
@ -79,18 +78,12 @@ func (client *providerClient) GetNameservers(domain string) ([]*models.Nameserve
func (client *providerClient) GetZoneRecordsCorrections(dc *models.DomainConfig, foundRecords models.Records) ([]*models.Correction, error) {
//txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records
var corrections []*models.Correction
var err error
var differ diff.Differ
if !diff2.EnableDiff2 {
differ = diff.New(dc)
} else {
differ = diff.NewCompat(dc)
}
_, creates, dels, modifications, err := differ.IncrementalDiff(foundRecords)
toReport, creates, dels, modifications, err := diff.NewCompat(dc).IncrementalDiff(foundRecords)
if err != nil {
return nil, err
}
// Start corrections with the reports
corrections := diff.GenerateMessageCorrections(toReport)
// CSCGlobal has a unique API. A list of edits is sent in one API
// call. Edits aren't permitted if an existing edit is being

View file

@ -8,7 +8,6 @@ import (
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/pkg/printer"
"github.com/StackExchange/dnscontrol/v4/pkg/txtutil"
"github.com/StackExchange/dnscontrol/v4/providers"
@ -164,20 +163,14 @@ func (c *desecProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, exist
c.mutex.Unlock()
PrepDesiredRecords(dc, minTTL)
var corrections []*models.Correction
var err error
var keysToUpdate map[models.RecordKey][]string
if !diff2.EnableDiff2 {
// diff existing vs. current.
keysToUpdate, err = (diff.New(dc)).ChangedGroups(existing)
} else {
keysToUpdate, err = (diff.NewCompat(dc)).ChangedGroups(existing)
}
keysToUpdate, toReport, err := diff.NewCompat(dc).ChangedGroups(existing)
if err != nil {
return nil, err
}
// Start corrections with the reports
corrections := diff.GenerateMessageCorrections(toReport)
if len(keysToUpdate) == 0 {
if len(corrections) == 0 && len(keysToUpdate) == 0 {
return nil, nil
}

View file

@ -10,7 +10,6 @@ import (
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/pkg/txtutil"
"github.com/StackExchange/dnscontrol/v4/providers"
"github.com/digitalocean/godo"
@ -173,17 +172,12 @@ func (api *digitaloceanProvider) GetZoneRecordsCorrections(dc *models.DomainConf
ctx := context.Background()
var corrections []*models.Correction
var differ diff.Differ
if !diff2.EnableDiff2 {
differ = diff.New(dc)
} else {
differ = diff.NewCompat(dc)
}
_, toCreate, toDelete, toModify, err := differ.IncrementalDiff(existingRecords)
toReport, toCreate, toDelete, toModify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords)
if err != nil {
return nil, err
}
// Start corrections with the reports
corrections := diff.GenerateMessageCorrections(toReport)
// Deletes first so changing type works etc.
for _, m := range toDelete {

View file

@ -11,7 +11,6 @@ import (
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/pkg/printer"
"github.com/StackExchange/dnscontrol/v4/providers"
dnsimpleapi "github.com/dnsimple/dnsimple-go/dnsimple"
@ -140,26 +139,21 @@ func (c *dnsimpleProvider) GetZoneRecords(domain string, meta map[string]string)
}
func (c *dnsimpleProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, actual models.Records) ([]*models.Correction, error) {
var corrections []*models.Correction
removeOtherApexNS(dc)
dnssecFixes, err := c.getDNSSECCorrections(dc)
if err != nil {
return nil, err
}
corrections = append(corrections, dnssecFixes...)
var differ diff.Differ
if !diff2.EnableDiff2 {
differ = diff.New(dc)
} else {
differ = diff.NewCompat(dc)
}
_, create, del, modify, err := differ.IncrementalDiff(actual)
toReport, create, del, modify, err := diff.NewCompat(dc).IncrementalDiff(actual)
if err != nil {
return nil, err
}
// Start corrections with the reports
corrections := diff.GenerateMessageCorrections(toReport)
// Next dnsSec fixes
corrections = append(corrections, dnssecFixes...)
for _, del := range del {
rec := del.Existing.Original.(dnsimpleapi.ZoneRecord)

View file

@ -8,7 +8,6 @@ import (
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/pkg/txtutil"
"github.com/StackExchange/dnscontrol/v4/providers"
)
@ -118,17 +117,12 @@ func (api *dnsMadeEasyProvider) GetZoneRecordsCorrections(dc *models.DomainConfi
}
}
var corrections []*models.Correction
var differ diff.Differ
if !diff2.EnableDiff2 {
differ = diff.New(dc)
} else {
differ = diff.NewCompat(dc)
}
_, create, del, modify, err := differ.IncrementalDiff(existingRecords)
toReport, create, del, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords)
if err != nil {
return nil, err
}
// Start corrections with the reports
corrections := diff.GenerateMessageCorrections(toReport)
var deleteRecordIds []int
deleteDescription := []string{"Batch deletion of records:"}

View file

@ -7,7 +7,6 @@ import (
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
)
func (api *domainNameShopProvider) GetZoneRecords(domain string, meta map[string]string) (models.Records, error) {
@ -40,17 +39,12 @@ func (api *domainNameShopProvider) GetZoneRecordsCorrections(dc *models.DomainCo
record.TTL = fixTTL(record.TTL)
}
var corrections []*models.Correction
var differ diff.Differ
if !diff2.EnableDiff2 {
differ = diff.New(dc)
} else {
differ = diff.NewCompat(dc)
}
_, create, delete, modify, err := differ.IncrementalDiff(existingRecords)
toReport, create, delete, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords)
if err != nil {
return nil, err
}
// Start corrections with the reports
corrections := diff.GenerateMessageCorrections(toReport)
// Delete record
for _, r := range delete {

View file

@ -12,7 +12,6 @@ import (
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/pkg/printer"
"github.com/StackExchange/dnscontrol/v4/providers"
)
@ -191,17 +190,12 @@ func (c *exoscaleProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, ex
}
domainID := *domain.ID
var corrections []*models.Correction
var differ diff.Differ
if !diff2.EnableDiff2 {
differ = diff.New(dc)
} else {
differ = diff.NewCompat(dc)
}
_, create, toDelete, modify, err := differ.IncrementalDiff(existingRecords)
toReport, create, toDelete, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords)
if err != nil {
return nil, err
}
// Start corrections with the reports
corrections := diff.GenerateMessageCorrections(toReport)
for _, del := range toDelete {
record := del.Existing.Original.(*egoscale.DNSDomainRecord)

View file

@ -22,7 +22,6 @@ import (
"strings"
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/pkg/printer"
"github.com/StackExchange/dnscontrol/v4/pkg/txtutil"
@ -193,6 +192,7 @@ func PrepDesiredRecords(dc *models.DomainConfig) {
// GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records.
func (client *gandiv5Provider) GetZoneRecordsCorrections(dc *models.DomainConfig, existing models.Records) ([]*models.Correction, error) {
var corrections []*models.Correction
if client.debug {
debugRecords("GenDC input", existing)
}
@ -200,117 +200,6 @@ func (client *gandiv5Provider) GetZoneRecordsCorrections(dc *models.DomainConfig
PrepDesiredRecords(dc)
txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records
var corrections []*models.Correction
if !diff2.EnableDiff2 {
// diff existing vs. current.
differ := diff.New(dc)
keysToUpdate, err := differ.ChangedGroups(existing)
if err != nil {
return nil, err
}
if client.debug {
diff.DebugKeyMapMap("GenDC diff", keysToUpdate)
}
if len(keysToUpdate) == 0 {
return nil, nil
}
// Regroup data by FQDN. ChangedGroups returns data grouped by label:RType tuples.
affectedLabels, msgsForLabel := gatherAffectedLabels(keysToUpdate)
_, desiredRecords := dc.Records.GroupedByFQDN()
doesLabelExist := existing.FQDNMap()
g := gandi.NewLiveDNSClient(config.Config{
APIKey: client.apikey,
SharingID: client.sharingid,
Debug: client.debug,
})
// For any key with an update, delete or replace those records.
for label := range affectedLabels {
if len(desiredRecords[label]) == 0 {
// No records matching this key? This can only mean that all
// the records were deleted. Delete them.
msgs := strings.Join(msgsForLabel[label], "\n")
domain := dc.Name
shortname := dnsutil.TrimDomainName(label, dc.Name)
corrections = append(corrections,
&models.Correction{
Msg: msgs,
F: func() error {
err := g.DeleteDomainRecordsByName(domain, shortname)
if err != nil {
return err
}
return nil
},
})
} else {
// Replace all the records at a label with our new records.
// Generate the new data in Gandi's format.
ns := recordsToNative(desiredRecords[label], dc.Name)
if doesLabelExist[label] {
// Records exist for this label. Replace them with what we have.
msg := strings.Join(msgsForLabel[label], "\n")
domain := dc.Name
shortname := dnsutil.TrimDomainName(label, dc.Name)
corrections = append(corrections,
&models.Correction{
Msg: msg,
F: func() error {
res, err := g.UpdateDomainRecordsByName(domain, shortname, ns)
if err != nil {
return fmt.Errorf("%+v: %w", res, err)
}
return nil
},
})
} else {
// First time putting data on this label. Create it.
// We have to create the label one rtype at a time.
ns := recordsToNative(desiredRecords[label], dc.Name)
for _, n := range ns {
msg := strings.Join(msgsForLabel[label], "\n")
domain := dc.Name
shortname := dnsutil.TrimDomainName(label, dc.Name)
rtype := n.RrsetType
ttl := n.RrsetTTL
values := n.RrsetValues
corrections = append(corrections,
&models.Correction{
Msg: msg,
F: func() error {
res, err := g.CreateDomainRecord(domain, shortname, rtype, ttl, values)
if err != nil {
return fmt.Errorf("%+v: %w", res, err)
}
return nil
},
})
}
}
}
}
// NB(tlim): This sort is just to make updates look pretty. It is
// cosmetic. The risk here is that there may be some updates that
// require a specific order (for example a delete before an add).
// However the code doesn't seem to have such situation. All tests
// pass. That said, if this breaks anything, the easiest fix might
// be to just remove the sort.
sort.Slice(corrections, func(i, j int) bool { return diff.CorrectionLess(corrections, i, j) })
return corrections, nil
}
g := gandi.NewLiveDNSClient(config.Config{
APIKey: client.apikey,
SharingID: client.sharingid,
@ -408,19 +297,6 @@ func debugRecords(note string, recs []*models.RecordConfig) {
}
}
// gatherAffectedLabels takes the output of diff.ChangedGroups and
// regroups it by FQDN of the label, not by Key. It also returns
// a list of all the FQDNs.
func gatherAffectedLabels(groups map[models.RecordKey][]string) (labels map[string]bool, msgs map[string][]string) {
labels = map[string]bool{}
msgs = map[string][]string{}
for k, v := range groups {
labels[k.NameFQDN] = true
msgs[k.NameFQDN] = append(msgs[k.NameFQDN], v...)
}
return labels, msgs
}
// Section 3: Registrar-related functions
// GetNameservers returns a list of nameservers for domain.

View file

@ -11,7 +11,6 @@ import (
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/pkg/printer"
"github.com/StackExchange/dnscontrol/v4/pkg/txtutil"
"github.com/StackExchange/dnscontrol/v4/providers"
@ -61,7 +60,7 @@ type gcloudProvider struct {
project string
nameServerSet *string
zones map[string]*gdns.ManagedZone
// diff1
// For use with diff / NewComnpat()
oldRRsMap map[string]map[key]*gdns.ResourceRecordSet
zoneNameMap map[string]string
// provider metadata fields
@ -260,7 +259,6 @@ type correctionValues struct {
// GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records.
func (g *gcloudProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, error) {
txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records
oldRRs, ok := g.oldRRsMap[dc.Name]
@ -273,22 +271,20 @@ func (g *gcloudProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, exis
}
// first collect keys that have changed
var differ diff.Differ
if !diff2.EnableDiff2 {
differ = diff.New(dc)
} else {
differ = diff.NewCompat(dc)
}
_, create, delete, modify, err := differ.IncrementalDiff(existingRecords)
toReport, create, toDelete, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords)
if err != nil {
return nil, fmt.Errorf("incdiff error: %w", err)
}
// Start corrections with the reports
corrections := diff.GenerateMessageCorrections(toReport)
// Now generate all other corrections
changedKeys := map[key]string{}
for _, c := range create {
changedKeys[keyForRec(c.Desired)] = fmt.Sprintln(c)
}
for _, d := range delete {
for _, d := range toDelete {
changedKeys[keyForRec(d.Existing)] = fmt.Sprintln(d)
}
for _, m := range modify {
@ -373,7 +369,6 @@ func (g *gcloudProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, exis
}
// create a Correction for each gdns.Change
// that needs to be executed
corrections := []*models.Correction{}
makeCorrection := func(chg *gdns.Change, msgs string) {
runChange := func() error {
retry:

View file

@ -7,7 +7,6 @@ import (
"strings"
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/providers"
@ -137,119 +136,46 @@ func (c *gcoreProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, exist
var deletions []*models.Correction
var reports []*models.Correction
if !diff2.EnableDiff2 {
changes, err := diff2.ByRecordSet(existing, dc, nil)
if err != nil {
return nil, err
}
// diff existing vs. current.
differ := diff.New(dc)
keysToUpdate, err := differ.ChangedGroups(existing)
if err != nil {
return nil, err
}
if len(keysToUpdate) == 0 {
return nil, nil
}
for _, change := range changes {
record := recordsToNative(change.New, change.Key)
desiredRecords := dc.Records.GroupedByKey()
existingRecords := existing.GroupedByKey()
// Copy all params to avoid overwrites
zone := dc.Name
name := change.Key.NameFQDN
typ := change.Key.Type
msg := generateChangeMsg(change.Msgs)
for label := range keysToUpdate {
if _, ok := desiredRecords[label]; !ok {
// record deleted in update
// Copy all params to avoid overwrites
zone := dc.Name
name := label.NameFQDN
typ := label.Type
msg := generateChangeMsg(keysToUpdate[label])
deletions = append(deletions, &models.Correction{
Msg: msg,
F: func() error {
return c.provider.DeleteRRSet(c.ctx, zone, name, typ)
},
})
} else if _, ok := existingRecords[label]; !ok {
// record created in update
record := recordsToNative(desiredRecords[label], label)
if record == nil {
panic("No records matching label")
}
// Copy all params to avoid overwrites
zone := dc.Name
name := label.NameFQDN
typ := label.Type
msg := generateChangeMsg(keysToUpdate[label])
corrections = append(corrections, &models.Correction{
Msg: msg,
F: func() error {
return c.provider.CreateRRSet(c.ctx, zone, name, typ, *record)
},
})
} else {
// record modified in update
record := recordsToNative(desiredRecords[label], label)
if record == nil {
panic("No records matching label")
}
// Copy all params to avoid overwrites
zone := dc.Name
name := label.NameFQDN
typ := label.Type
msg := generateChangeMsg(keysToUpdate[label])
corrections = append(corrections, &models.Correction{
Msg: msg,
F: func() error {
return c.provider.UpdateRRSet(c.ctx, zone, name, typ, *record)
},
})
}
}
} else {
// Diff2 version
changes, err := diff2.ByRecordSet(existing, dc, nil)
if err != nil {
return nil, err
}
for _, change := range changes {
record := recordsToNative(change.New, change.Key)
// Copy all params to avoid overwrites
zone := dc.Name
name := change.Key.NameFQDN
typ := change.Key.Type
msg := generateChangeMsg(change.Msgs)
switch change.Type {
case diff2.REPORT:
corrections = append(corrections, &models.Correction{Msg: change.MsgsJoined})
case diff2.CREATE:
corrections = append(corrections, &models.Correction{
Msg: msg,
F: func() error {
return c.provider.CreateRRSet(c.ctx, zone, name, typ, *record)
},
})
case diff2.CHANGE:
corrections = append(corrections, &models.Correction{
Msg: msg,
F: func() error {
return c.provider.UpdateRRSet(c.ctx, zone, name, typ, *record)
},
})
case diff2.DELETE:
deletions = append(deletions, &models.Correction{
Msg: msg,
F: func() error {
return c.provider.DeleteRRSet(c.ctx, zone, name, typ)
},
})
default:
panic(fmt.Sprintf("unhandled change.Type %s", change.Type))
}
switch change.Type {
case diff2.REPORT:
corrections = append(corrections, &models.Correction{Msg: change.MsgsJoined})
case diff2.CREATE:
corrections = append(corrections, &models.Correction{
Msg: msg,
F: func() error {
return c.provider.CreateRRSet(c.ctx, zone, name, typ, *record)
},
})
case diff2.CHANGE:
corrections = append(corrections, &models.Correction{
Msg: msg,
F: func() error {
return c.provider.UpdateRRSet(c.ctx, zone, name, typ, *record)
},
})
case diff2.DELETE:
deletions = append(deletions, &models.Correction{
Msg: msg,
F: func() error {
return c.provider.DeleteRRSet(c.ctx, zone, name, typ)
},
})
default:
panic(fmt.Sprintf("unhandled change.Type %s", change.Type))
}
}

View file

@ -16,7 +16,6 @@ import (
"github.com/PuerkitoBio/goquery"
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/pkg/txtutil"
"github.com/StackExchange/dnscontrol/v4/providers"
@ -195,50 +194,9 @@ func (c *hednsProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, recor
// Normalize
txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records
// Fallback to legacy mode if diff2 is not enabled, remove when diff1 is deprecated.
if !diff2.EnableDiff2 {
return c.getDiff1DomainCorrections(dc, zoneID, prunedRecords)
}
return c.getDiff2DomainCorrections(dc, zoneID, prunedRecords)
}
func (c *hednsProvider) getDiff1DomainCorrections(dc *models.DomainConfig, zoneID uint64, records models.Records) ([]*models.Correction, error) {
var corrections []*models.Correction
differ := diff.New(dc)
_, toCreate, toDelete, toModify, err := differ.IncrementalDiff(records)
if err != nil {
return nil, err
}
for _, del := range toDelete {
recordID := del.Existing.Original.(Record).RecordID
corrections = append(corrections, &models.Correction{
Msg: del.String(),
F: func() error { return c.deleteZoneRecord(zoneID, recordID) },
})
}
for _, cre := range toCreate {
record := cre.Desired
corrections = append(corrections, &models.Correction{
Msg: cre.String(),
F: func() error { return c.createZoneRecord(zoneID, record) },
})
}
for _, mod := range toModify {
record := mod.Desired
recordID := mod.Existing.Original.(Record).RecordID
corrections = append(corrections, &models.Correction{
Msg: mod.String(),
F: func() error { return c.changeZoneRecord(zoneID, recordID, record) },
})
}
return corrections, nil
}
func (c *hednsProvider) getDiff2DomainCorrections(dc *models.DomainConfig, zoneID uint64, records models.Records) ([]*models.Correction, error) {
changes, err := diff2.ByRecord(records, dc, nil)
if err != nil {

View file

@ -7,7 +7,6 @@ import (
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/pkg/txtutil"
"github.com/StackExchange/dnscontrol/v4/providers"
)
@ -75,24 +74,18 @@ func (api *hetznerProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, e
txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records
var corrections []*models.Correction
var differ diff.Differ
if !diff2.EnableDiff2 {
differ = diff.New(dc)
} else {
differ = diff.NewCompat(dc)
}
_, create, del, modify, err := differ.IncrementalDiff(existingRecords)
toReport, create, del, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords)
if err != nil {
return nil, err
}
// Start corrections with the reports
corrections := diff.GenerateMessageCorrections(toReport)
z, err := api.getZone(domain)
if err != nil {
return nil, err
}
corrections = make([]*models.Correction, 0, len(del)+1+1)
for _, m := range del {
r := m.Existing.Original.(*record)
corr := &models.Correction{

View file

@ -9,7 +9,6 @@ import (
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/pkg/txtutil"
)
@ -59,27 +58,14 @@ func (n *HXClient) GetZoneRecords(domain string, meta map[string]string) (models
// GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records.
func (n *HXClient) GetZoneRecordsCorrections(dc *models.DomainConfig, actual models.Records) ([]*models.Correction, error) {
// actual, err := n.GetZoneRecords(dc.Name)
// if err != nil {
// return nil, err
// }
//checkNSModifications(dc)
txtutil.SplitSingleLongTxt(dc.Records)
var corrections []*models.Correction
var differ diff.Differ
if !diff2.EnableDiff2 {
differ = diff.New(dc)
} else {
differ = diff.NewCompat(dc)
}
_, create, del, mod, err := differ.IncrementalDiff(actual)
toReport, create, del, mod, err := diff.NewCompat(dc).IncrementalDiff(actual)
if err != nil {
return nil, err
}
// Start corrections with the reports
corrections := diff.GenerateMessageCorrections(toReport)
buf := &bytes.Buffer{}
// Print a list of changes. Generate an actual change that is the zone

View file

@ -10,7 +10,6 @@ import (
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/providers"
)
@ -138,19 +137,16 @@ func (hp *hostingdeProvider) GetZoneRecordsCorrections(dc *models.DomainConfig,
return nil, err
}
var create, del, mod diff.Changeset
if !diff2.EnableDiff2 {
_, create, del, mod, err = diff.New(dc).IncrementalDiff(records)
} else {
_, create, del, mod, err = diff.NewCompat(dc).IncrementalDiff(records)
}
toReport, create, del, mod, err := diff.NewCompat(dc).IncrementalDiff(records)
if err != nil {
return nil, err
}
// Start corrections with the reports
corrections := diff.GenerateMessageCorrections(toReport)
// NOPURGE
if dc.KeepUnknown {
del = []diff.Correlation{}
del = nil
}
// remove SOA record from corrections as it is handled separately
@ -267,31 +263,30 @@ func (hp *hostingdeProvider) GetZoneRecordsCorrections(dc *models.DomainConfig,
return nil, nil
}
corrections := []*models.Correction{
{
Msg: fmt.Sprintf("\n%s", strings.Join(msg, "\n")),
F: func() error {
for i := 0; i < 10; i++ {
err := hp.updateZone(&zone.ZoneConfig, DNSSecOptions, create, del, mod)
if err == nil {
return nil
}
// Code:10205 indicates the zone is currently blocked due to a running zone update.
if !strings.Contains(err.Error(), "Code:10205") {
return err
}
// Exponential back-off retry.
// Base of 1.8 seemed like a good trade-off, retrying for approximately 45 seconds.
time.Sleep(time.Duration(math.Pow(1.8, float64(i))) * 100 * time.Millisecond)
corrections = append(corrections, &models.Correction{
Msg: fmt.Sprintf("\n%s", strings.Join(msg, "\n")),
F: func() error {
for i := 0; i < 10; i++ {
err := hp.updateZone(&zone.ZoneConfig, DNSSecOptions, create, del, mod)
if err == nil {
return nil
}
return fmt.Errorf("retry exhaustion: zone blocked for 10 attempts")
},
// Code:10205 indicates the zone is currently blocked due to a running zone update.
if !strings.Contains(err.Error(), "Code:10205") {
return err
}
// Exponential back-off retry.
// Base of 1.8 seemed like a good trade-off, retrying for approximately 45 seconds.
time.Sleep(time.Duration(math.Pow(1.8, float64(i))) * 100 * time.Millisecond)
}
return fmt.Errorf("retry exhaustion: zone blocked for 10 attempts")
},
}
},
)
if removeDNSSecEntries != nil {
correction := models.Correction{
correction := &models.Correction{
Msg: "Removing AutoDNSSEC Keys from Domain",
F: func() error {
err := hp.dnsSecKeyModify(dc.Name, nil, removeDNSSecEntries)
@ -301,7 +296,7 @@ func (hp *hostingdeProvider) GetZoneRecordsCorrections(dc *models.DomainConfig,
return nil
},
}
corrections = append(corrections, &correction)
corrections = append(corrections, correction)
}
return corrections, nil

View file

@ -9,7 +9,6 @@ import (
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/pkg/printer"
"github.com/StackExchange/dnscontrol/v4/pkg/txtutil"
"github.com/StackExchange/dnscontrol/v4/providers"
@ -236,17 +235,12 @@ func (api *inwxAPI) GetZoneRecordsCorrections(dc *models.DomainConfig, foundReco
return nil, err
}
var corrections []*models.Correction
var differ diff.Differ
if !diff2.EnableDiff2 {
differ = diff.New(dc)
} else {
differ = diff.NewCompat(dc)
}
_, create, del, mod, err := differ.IncrementalDiff(foundRecords)
toReport, create, del, mod, err := diff.NewCompat(dc).IncrementalDiff(foundRecords)
if err != nil {
return nil, err
}
// Start corrections with the reports
corrections := diff.GenerateMessageCorrections(toReport)
for _, d := range create {
des := d.Desired

View file

@ -11,7 +11,6 @@ import (
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/providers"
"github.com/miekg/dns/dnsutil"
"golang.org/x/oauth2"
@ -135,9 +134,8 @@ func (api *linodeProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, ex
record.TTL = fixTTL(record.TTL)
}
var err error
if api.domainIndex == nil {
if err = api.fetchDomainList(); err != nil {
if err := api.fetchDomainList(); err != nil {
return nil, err
}
}
@ -146,17 +144,12 @@ func (api *linodeProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, ex
return nil, fmt.Errorf("'%s' not a zone in Linode account", dc.Name)
}
var corrections []*models.Correction
var differ diff.Differ
if !diff2.EnableDiff2 {
differ = diff.New(dc)
} else {
differ = diff.NewCompat(dc)
}
_, create, del, modify, err := differ.IncrementalDiff(existingRecords)
toReport, create, del, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords)
if err != nil {
return nil, err
}
// Start corrections with the reports
corrections := diff.GenerateMessageCorrections(toReport)
// Deletes first so changing type works etc.
for _, m := range del {

View file

@ -24,7 +24,6 @@ import (
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/pkg/printer"
"github.com/StackExchange/dnscontrol/v4/pkg/txtutil"
"github.com/StackExchange/dnscontrol/v4/providers"
@ -267,6 +266,7 @@ func gatherAffectedLabels(groups map[models.RecordKey][]string) (labels map[stri
// GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records.
func (c *APIClient) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, error) {
if c.Debug {
debugRecords("GenerateZoneRecordsCorrections input:\n", existingRecords)
}
@ -275,19 +275,16 @@ func (c *APIClient) GetZoneRecordsCorrections(dc *models.DomainConfig, existingR
txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records
PrepDesiredRecords(dc)
var corrections []*models.Correction
var keysToUpdate map[models.RecordKey][]string
var differ diff.Differ
if !diff2.EnableDiff2 {
differ = diff.New(dc)
} else {
differ = diff.NewCompat(dc)
}
_, create, del, modify, err := differ.IncrementalDiff(existingRecords)
differ := diff.NewCompat(dc)
toReport, create, del, modify, err := differ.IncrementalDiff(existingRecords)
if err != nil {
return nil, err
}
keysToUpdate, err = differ.ChangedGroups(existingRecords)
// Start corrections with the reports
corrections := diff.GenerateMessageCorrections(toReport)
keysToUpdate, _, err = differ.ChangedGroups(existingRecords)
if err != nil {
return nil, err
}

View file

@ -5,7 +5,6 @@ import (
"fmt"
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/providers"
)
@ -96,6 +95,7 @@ func (l *luadnsProvider) GetZoneRecords(domain string, meta map[string]string) (
// GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records.
func (l *luadnsProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, records models.Records) ([]*models.Correction, error) {
var corrections []*models.Correction
checkNS(dc)
@ -104,33 +104,7 @@ func (l *luadnsProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, reco
return nil, err
}
var corrections []*models.Correction
var corrs []*models.Correction
if !diff2.EnableDiff2 {
differ := diff.New(dc)
_, create, del, mod, err := differ.IncrementalDiff(records)
if err != nil {
return nil, err
}
corrections := []*models.Correction{}
for _, d := range del {
corrs := l.makeDeleteCorrection(d.Existing, domainID, d.String())
corrections = append(corrections, corrs...)
}
for _, d := range create {
corrs := l.makeCreateCorrection(d.Desired, domainID, d.String())
corrections = append(corrections, corrs...)
}
for _, d := range mod {
corrs := l.makeChangeCorrection(d.Existing, d.Desired, domainID, d.String())
corrections = append(corrections, corrs...)
}
return corrections, nil
}
changes, err := diff2.ByRecord(records, dc, nil)
if err != nil {

View file

@ -4,39 +4,18 @@ import (
"fmt"
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/pkg/txtutil"
)
// GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records.
func (client *msdnsProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, foundRecords models.Records) ([]*models.Correction, error) {
var corrections []*models.Correction
// Normalize
models.PostProcessRecords(foundRecords)
txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records
var corrections []*models.Correction
if !diff2.EnableDiff2 {
differ := diff.New(dc)
_, creates, dels, modifications, err := differ.IncrementalDiff(foundRecords)
if err != nil {
return nil, err
}
// Generate changes.
for _, del := range dels {
corrections = append(corrections, client.deleteRec(client.dnsserver, dc.Name, del))
}
for _, cre := range creates {
corrections = append(corrections, client.createRec(client.dnsserver, dc.Name, cre)...)
}
for _, m := range modifications {
corrections = append(corrections, client.modifyRec(client.dnsserver, dc.Name, m))
}
return corrections, nil
}
changes, err := diff2.ByRecord(foundRecords, dc, nil)
if err != nil {
return nil, err
@ -108,34 +87,3 @@ func (client *msdnsProvider) modifyOneRecord(dnsserver, zonename string, oldrec,
func (client *msdnsProvider) modifyRecordTTL(dnsserver, zonename string, oldrec, newrec *models.RecordConfig) error {
return client.shell.RecordModifyTTL(dnsserver, zonename, oldrec, newrec.TTL)
}
func (client *msdnsProvider) deleteRec(dnsserver, domainname string, cor diff.Correlation) *models.Correction {
rec := cor.Existing
return &models.Correction{
Msg: cor.String(),
F: func() error {
return client.shell.RecordDelete(dnsserver, domainname, rec)
},
}
}
func (client *msdnsProvider) createRec(dnsserver, domainname string, cre diff.Correlation) []*models.Correction {
rec := cre.Desired
arr := []*models.Correction{{
Msg: cre.String(),
F: func() error {
return client.shell.RecordCreate(dnsserver, domainname, rec)
},
}}
return arr
}
func (client *msdnsProvider) modifyRec(dnsserver, domainname string, m diff.Correlation) *models.Correction {
old, rec := m.Existing, m.Desired
return &models.Correction{
Msg: m.String(),
F: func() error {
return client.shell.RecordModify(dnsserver, domainname, old, rec)
},
}
}

View file

@ -9,7 +9,6 @@ import (
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/pkg/printer"
"github.com/StackExchange/dnscontrol/v4/providers"
nc "github.com/billputer/go-namecheap"
@ -245,16 +244,12 @@ func (n *namecheapProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, a
return true
})
var differ diff.Differ
if !diff2.EnableDiff2 {
differ = diff.New(dc)
} else {
differ = diff.NewCompat(dc)
}
_, create, delete, modify, err := differ.IncrementalDiff(actual)
toReport, create, delete, modify, err := diff.NewCompat(dc).IncrementalDiff(actual)
if err != nil {
return nil, err
}
// Start corrections with the reports
corrections := diff.GenerateMessageCorrections(toReport)
// because namecheap doesn't have selective create, delete, modify,
// we bundle them all up to send at once. We *do* want to see the
@ -271,11 +266,9 @@ func (n *namecheapProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, a
desc = append(desc, "\n"+i.String())
}
msg := fmt.Sprintf("GENERATE_ZONE: %s (%d records)%s", dc.Name, len(dc.Records), desc)
var corrections []*models.Correction
// only create corrections if there are changes
if len(desc) > 0 {
msg := fmt.Sprintf("GENERATE_ZONE: %s (%d records)%s", dc.Name, len(dc.Records), desc)
corrections = append(corrections,
&models.Correction{
Msg: msg,

View file

@ -8,7 +8,6 @@ import (
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/namedotcom/go/namecom"
)
@ -29,7 +28,6 @@ func (n *namedotcomProvider) GetZoneRecords(domain string, meta map[string]strin
// GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records.
func (n *namedotcomProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, actual models.Records) ([]*models.Correction, error) {
checkNSModifications(dc)
for _, rec := range dc.Records {
@ -38,17 +36,12 @@ func (n *namedotcomProvider) GetZoneRecordsCorrections(dc *models.DomainConfig,
}
}
var corrections []*models.Correction
var differ diff.Differ
if !diff2.EnableDiff2 {
differ = diff.New(dc)
} else {
differ = diff.NewCompat(dc)
}
_, create, del, mod, err := differ.IncrementalDiff(actual)
toReport, create, del, mod, err := diff.NewCompat(dc).IncrementalDiff(actual)
if err != nil {
return nil, err
}
// Start corrections with the reports
corrections := diff.GenerateMessageCorrections(toReport)
for _, d := range del {
rec := d.Existing.Original.(*namecom.Record)

View file

@ -6,7 +6,6 @@ import (
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/providers"
)
@ -89,17 +88,12 @@ func (api *netcupProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, ex
}
dc.Records = newRecords
var corrections []*models.Correction
var differ diff.Differ
if !diff2.EnableDiff2 {
differ = diff.New(dc)
} else {
differ = diff.NewCompat(dc)
}
_, create, del, modify, err := differ.IncrementalDiff(existingRecords)
toReport, create, del, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords)
if err != nil {
return nil, err
}
// Start corrections with the reports
corrections := diff.GenerateMessageCorrections(toReport)
// Deletes first so changing type works etc.
for _, m := range del {

View file

@ -7,7 +7,6 @@ import (
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/providers"
"github.com/miekg/dns"
)
@ -141,17 +140,12 @@ func (n *netlifyProvider) GetZoneRecords(domain string, meta map[string]string)
// GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records.
func (n *netlifyProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, records models.Records) ([]*models.Correction, error) {
var corrections []*models.Correction
var differ diff.Differ
if !diff2.EnableDiff2 {
differ = diff.New(dc)
} else {
differ = diff.NewCompat(dc)
}
_, create, del, modify, err := differ.IncrementalDiff(records)
toReport, create, del, modify, err := diff.NewCompat(dc).IncrementalDiff(records)
if err != nil {
return nil, err
}
// Start corrections with the reports
corrections := diff.GenerateMessageCorrections(toReport)
zone, err := n.getZone(dc.Name)
if err != nil {

View file

@ -8,7 +8,6 @@ import (
"strings"
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/providers"
"gopkg.in/ns1/ns1-go.v2/rest"
@ -188,7 +187,7 @@ func (n *nsone) getDomainCorrectionsDNSSEC(domain, toggleDNSSEC string) *models.
// GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records.
func (n *nsone) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, error) {
corrections := []*models.Correction{}
var corrections []*models.Correction
domain := dc.Name
// add DNSSEC-related corrections
@ -196,49 +195,6 @@ func (n *nsone) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecor
corrections = append(corrections, dnssecCorrections)
}
if !diff2.EnableDiff2 {
existingGrouped := existingRecords.GroupedByKey()
desiredGrouped := dc.Records.GroupedByKey()
differ := diff.New(dc)
changedGroups, err := differ.ChangedGroups(existingRecords)
if err != nil {
return nil, err
}
// each name/type is given to the api as a unit.
for k, descs := range changedGroups {
key := k
desc := strings.Join(descs, "\n")
_, current := existingGrouped[k]
recs, wanted := desiredGrouped[k]
if wanted && !current {
// pure addition
corrections = append(corrections, &models.Correction{
Msg: desc,
F: func() error { return n.add(recs, dc.Name) },
})
} else if current && !wanted {
// pure deletion
corrections = append(corrections, &models.Correction{
Msg: desc,
F: func() error { return n.remove(key, dc.Name) },
})
} else {
// modification
corrections = append(corrections, &models.Correction{
Msg: desc,
F: func() error { return n.modify(recs, dc.Name) },
})
}
}
return corrections, nil
}
changes, err := diff2.ByRecordSet(existingRecords, dc, nil)
if err != nil {
return nil, err

View file

@ -8,7 +8,6 @@ import (
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/pkg/printer"
"github.com/StackExchange/dnscontrol/v4/pkg/txtutil"
"github.com/StackExchange/dnscontrol/v4/providers"
@ -225,16 +224,12 @@ func (o *oracleProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, exis
}
}
var differ diff.Differ
if !diff2.EnableDiff2 {
differ = diff.New(dc)
} else {
differ = diff.NewCompat(dc)
}
_, create, dels, modify, err := differ.IncrementalDiff(existingRecords)
toReport, create, dels, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords)
if err != nil {
return nil, err
}
// Start corrections with the reports
corrections := diff.GenerateMessageCorrections(toReport)
/*
Oracle's API doesn't have a way to update an existing record.
@ -273,15 +268,17 @@ func (o *oracleProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, exis
desc = desc[:len(desc)-1]
}
// There were corrections. Send them as one big batch:
if len(createRecords) > 0 || len(deleteRecords) > 0 {
return []*models.Correction{{
corrections = append(corrections, &models.Correction{
Msg: desc,
F: func() error {
return o.patch(createRecords, deleteRecords, dc.Name)
},
}}, nil
})
}
return []*models.Correction{}, nil
return corrections, nil
}
func (o *oracleProvider) patch(createRecords, deleteRecords models.Records, domain string) error {

View file

@ -7,7 +7,6 @@ import (
"strings"
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/providers"
"github.com/ovh/go-ovh/ovh"
@ -122,14 +121,7 @@ func (c *ovhProvider) GetZoneRecords(domain string, meta map[string]string) (mod
// GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records.
func (c *ovhProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, actual models.Records) ([]*models.Correction, error) {
var corrections []*models.Correction
var err error
if !diff2.EnableDiff2 {
corrections, err = c.getDiff1DomainCorrections(dc, actual)
} else {
corrections, err = c.getDiff2DomainCorrections(dc, actual)
}
corrections, err := c.getDiff2DomainCorrections(dc, actual)
if err != nil {
return nil, err
}
@ -146,42 +138,6 @@ func (c *ovhProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, actual
return corrections, nil
}
func (c *ovhProvider) getDiff1DomainCorrections(dc *models.DomainConfig, actual models.Records) ([]*models.Correction, error) {
var corrections []*models.Correction
differ := diff.New(dc)
_, create, delete, modify, err := differ.IncrementalDiff(actual)
if err != nil {
return nil, err
}
for _, del := range delete {
rec := del.Existing.Original.(*Record)
corrections = append(corrections, &models.Correction{
Msg: del.String(),
F: c.deleteRecordFunc(rec.ID, dc.Name),
})
}
for _, cre := range create {
rec := cre.Desired
corrections = append(corrections, &models.Correction{
Msg: cre.String(),
F: c.createRecordFunc(rec, dc.Name),
})
}
for _, mod := range modify {
oldR := mod.Existing.Original.(*Record)
newR := mod.Desired
corrections = append(corrections, &models.Correction{
Msg: mod.String(),
F: c.updateRecordFunc(oldR, newR, dc.Name),
})
}
return corrections, nil
}
func (c *ovhProvider) getDiff2DomainCorrections(dc *models.DomainConfig, actual models.Records) ([]*models.Correction, error) {
var corrections []*models.Correction
instructions, err := diff2.ByRecord(actual, dc, nil)

View file

@ -10,7 +10,6 @@ import (
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/providers"
)
@ -107,17 +106,12 @@ func (api *packetframeProvider) GetZoneRecordsCorrections(dc *models.DomainConfi
return nil, fmt.Errorf("no such zone %q in Packetframe account", dc.Name)
}
var corrections []*models.Correction
var differ diff.Differ
if !diff2.EnableDiff2 {
differ = diff.New(dc)
} else {
differ = diff.NewCompat(dc)
}
_, create, dels, modify, err := differ.IncrementalDiff(existingRecords)
toReport, create, dels, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords)
if err != nil {
return nil, err
}
// Start corrections with the reports
corrections := diff.GenerateMessageCorrections(toReport)
for _, m := range create {
req, err := toReq(zone.ID, dc, m.Desired)

View file

@ -8,7 +8,6 @@ import (
"strings"
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/pkg/printer"
"github.com/StackExchange/dnscontrol/v4/providers"
@ -87,6 +86,7 @@ func (c *porkbunProvider) GetNameservers(domain string) ([]*models.Nameserver, e
// GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records.
func (c *porkbunProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, error) {
var corrections []*models.Correction
// Block changes to NS records for base domain
checkNSModifications(dc)
@ -96,61 +96,6 @@ func (c *porkbunProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, exi
record.TTL = fixTTL(record.TTL)
}
var corrections []*models.Correction
if !diff2.EnableDiff2 {
differ := diff.New(dc)
_, create, del, modify, err := differ.IncrementalDiff(existingRecords)
if err != nil {
return nil, err
}
// Deletes first so changing type works etc.
for _, m := range del {
id := m.Existing.Original.(*domainRecord).ID
corr := &models.Correction{
Msg: fmt.Sprintf("%s, porkbun ID: %s", m.String(), id),
F: func() error {
return c.deleteRecord(dc.Name, id)
},
}
corrections = append(corrections, corr)
}
for _, m := range create {
req, err := toReq(m.Desired)
if err != nil {
return nil, err
}
corr := &models.Correction{
Msg: m.String(),
F: func() error {
return c.createRecord(dc.Name, req)
},
}
corrections = append(corrections, corr)
}
for _, m := range modify {
id := m.Existing.Original.(*domainRecord).ID
req, err := toReq(m.Desired)
if err != nil {
return nil, err
}
corr := &models.Correction{
Msg: fmt.Sprintf("%s, porkbun ID: %s", m.String(), id),
F: func() error {
return c.modifyRecord(dc.Name, id, req)
},
}
corrections = append(corrections, corr)
}
return corrections, nil
}
changes, err := diff2.ByRecord(existingRecords, dc, nil)
if err != nil {
return nil, err

View file

@ -3,71 +3,12 @@ package powerdns
import (
"context"
"fmt"
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/mittwald/go-powerdns/apis/zones"
"strings"
)
func (dsp *powerdnsProvider) getDiff1DomainCorrections(dc *models.DomainConfig, existing models.Records) ([]*models.Correction, error) {
// create record diff by group
keysToUpdate, err := (diff.New(dc)).ChangedGroups(existing)
if err != nil {
return nil, err
}
desiredRecords := dc.Records.GroupedByKey()
var cuCorrections []*models.Correction
var dCorrections []*models.Correction
// add create/update and delete corrections separately
for label, msgs := range keysToUpdate {
labelName := canonical(label.NameFQDN)
labelType := label.Type
msgJoined := strings.Join(msgs, "\n ")
if _, ok := desiredRecords[label]; !ok {
// no record found so delete it
dCorrections = append(dCorrections, &models.Correction{
Msg: msgJoined,
F: func() error {
return dsp.client.Zones().RemoveRecordSetFromZone(context.Background(), dsp.ServerName, dc.Name, labelName, labelType)
},
})
} else {
// record found so create or update it
ttl := desiredRecords[label][0].TTL
var records []zones.Record
for _, recordContent := range desiredRecords[label] {
records = append(records, zones.Record{
Content: recordContent.GetTargetCombined(),
})
}
cuCorrections = append(cuCorrections, &models.Correction{
Msg: msgJoined,
F: func() error {
return dsp.client.Zones().AddRecordSetToZone(context.Background(), dsp.ServerName, dc.Name, zones.ResourceRecordSet{
Name: labelName,
Type: labelType,
TTL: int(ttl),
Records: records,
ChangeType: zones.ChangeTypeReplace,
})
},
})
}
}
// append corrections in the right order
// delete corrections must be run first to avoid correlations with existing RR
var corrections []*models.Correction
corrections = append(corrections, dCorrections...)
corrections = append(corrections, cuCorrections...)
return corrections, nil
}
func (dsp *powerdnsProvider) getDiff2DomainCorrections(dc *models.DomainConfig, existing models.Records) ([]*models.Correction, error) {
changes, err := diff2.ByRecordSet(existing, dc, nil)
if err != nil {

View file

@ -5,7 +5,6 @@ import (
"net/http"
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/mittwald/go-powerdns/apis/zones"
"github.com/mittwald/go-powerdns/pdnshttp"
)
@ -47,15 +46,8 @@ func (dsp *powerdnsProvider) GetZoneRecords(domain string, meta map[string]strin
// GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records.
func (dsp *powerdnsProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existing models.Records) ([]*models.Correction, error) {
// create record diff by group
var err error
var corrections []*models.Correction
if !diff2.EnableDiff2 {
corrections, err = dsp.getDiff1DomainCorrections(dc, existing)
} else {
corrections, err = dsp.getDiff2DomainCorrections(dc, existing)
}
corrections, err := dsp.getDiff2DomainCorrections(dc, existing)
if err != nil {
return nil, err
}

View file

@ -12,7 +12,6 @@ import (
"unicode/utf8"
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/pkg/printer"
"github.com/StackExchange/dnscontrol/v4/pkg/txtutil"
@ -294,198 +293,6 @@ func (r *route53Provider) GetZoneRecordsCorrections(dc *models.DomainConfig, exi
}
var corrections []*models.Correction
if !diff2.EnableDiff2 {
zone, err := r.getZone(dc)
if err != nil {
return nil, err
}
// diff
differ := diff.New(dc, getAliasMap)
namesToUpdate, err := differ.ChangedGroups(existingRecords)
if err != nil {
return nil, err
}
if len(namesToUpdate) == 0 {
return nil, nil
}
updates := map[models.RecordKey][]*models.RecordConfig{}
// for each name we need to update, collect relevant records from our desired domain state
for k := range namesToUpdate {
updates[k] = nil
for _, rc := range dc.Records {
if rc.Key() == k {
updates[k] = append(updates[k], rc)
}
}
}
// updateOrder is the order that the updates will happen.
// The order should be sorted by NameFQDN, then Type, with R53_ALIAS_*
// types sorted after all other types. R53_ALIAS_* needs to be last
// because they are order dependent (aliases must refer to labels
// that already exist).
var updateOrder []models.RecordKey
// Collect the keys
for k := range updates {
updateOrder = append(updateOrder, k)
}
// Sort themm
sort.Slice(updateOrder, func(i, j int) bool {
if updateOrder[i].Type == updateOrder[j].Type {
return updateOrder[i].NameFQDN < updateOrder[j].NameFQDN
}
if strings.HasPrefix(updateOrder[i].Type, "R53_ALIAS_") {
return false
}
if strings.HasPrefix(updateOrder[j].Type, "R53_ALIAS_") {
return true
}
if updateOrder[i].NameFQDN == updateOrder[j].NameFQDN {
return updateOrder[i].Type < updateOrder[j].Type
}
return updateOrder[i].NameFQDN < updateOrder[j].NameFQDN
})
// we collect all changes into one of two categories now:
// pure deletions where we delete an entire record set,
// or changes where we upsert an entire record set.
dels := []r53Types.Change{}
delDesc := []string{}
changes := []r53Types.Change{}
changeDesc := []string{}
for _, currentKey := range updateOrder {
recs := updates[currentKey]
// If there are no records in our desired state for a key, this
// indicates we should delete all records at that key.
if len(recs) == 0 {
// To delete, we submit the original resource set we got from r53.
var (
rrset r53Types.ResourceRecordSet
found bool
)
// Find the original resource set:
for _, orec := range r.originalRecords {
if unescape(orec.Name) == currentKey.NameFQDN && (string(orec.Type) == currentKey.Type || currentKey.Type == "R53_ALIAS_"+string(orec.Type)) {
rrset = orec
found = true
break
}
}
if !found {
// This should not happen.
return nil, fmt.Errorf("no record set found to delete. Name: '%s'. Type: '%s'", currentKey.NameFQDN, currentKey.Type)
}
// Assemble the change and add it to the list:
chg := r53Types.Change{
Action: r53Types.ChangeActionDelete,
ResourceRecordSet: &rrset,
}
dels = append(dels, chg)
delDesc = append(delDesc, strings.Join(namesToUpdate[currentKey], "\n"))
} else {
// If it isn't a delete, it must be either a change or create. In
// either case, we build a new record set from the desired state and
// UPSERT it.
if strings.HasPrefix(currentKey.Type, "R53_ALIAS_") {
// Each R53_ALIAS_* requires an individual change.
if len(recs) != 1 {
log.Fatal("Only one R53_ALIAS_ permitted on a label")
}
for _, rec := range recs {
rrset := aliasToRRSet(zone, rec)
rrset.Name = aws.String(currentKey.NameFQDN)
// Assemble the change and add it to the list:
chg := r53Types.Change{
Action: r53Types.ChangeActionUpsert,
ResourceRecordSet: rrset,
}
changes = append(changes, chg)
changeDesc = append(changeDesc, strings.Join(namesToUpdate[currentKey], "\n"))
}
} else {
// All other keys combine their updates into one rrset:
rrset := &r53Types.ResourceRecordSet{
Name: aws.String(currentKey.NameFQDN),
Type: r53Types.RRType(currentKey.Type),
}
for _, rec := range recs {
val := rec.GetTargetCombined()
rr := r53Types.ResourceRecord{
Value: aws.String(val),
}
rrset.ResourceRecords = append(rrset.ResourceRecords, rr)
i := int64(rec.TTL)
rrset.TTL = &i // TODO: make sure that ttls are consistent within a set
}
// Assemble the change and add it to the list:
chg := r53Types.Change{
Action: r53Types.ChangeActionUpsert,
ResourceRecordSet: rrset,
}
changes = append(changes, chg)
changeDesc = append(changeDesc, strings.Join(namesToUpdate[currentKey], "\n"))
}
}
}
addCorrection := func(msg string, req *r53.ChangeResourceRecordSetsInput) {
corrections = append(corrections,
&models.Correction{
Msg: msg,
F: func() error {
var err error
req.HostedZoneId = zone.Id
withRetry(func() error {
_, err = r.client.ChangeResourceRecordSets(context.Background(), req)
return err
})
return err
},
})
}
batcher := newChangeBatcher(dels)
for batcher.Next() {
start, end := batcher.Batch()
batch := dels[start:end]
descBatchStr := "\n" + strings.Join(delDesc[start:end], "\n") + "\n"
req := &r53.ChangeResourceRecordSetsInput{
ChangeBatch: &r53Types.ChangeBatch{Changes: batch},
}
addCorrection(descBatchStr, req)
}
if err := batcher.Err(); err != nil {
return nil, err
}
batcher = newChangeBatcher(changes)
for batcher.Next() {
start, end := batcher.Batch()
batch := changes[start:end]
descBatchStr := "\n" + strings.Join(changeDesc[start:end], "\n") + "\n"
req := &r53.ChangeResourceRecordSetsInput{
ChangeBatch: &r53Types.ChangeBatch{Changes: batch},
}
addCorrection(descBatchStr, req)
}
if err := batcher.Err(); err != nil {
return nil, err
}
return corrections, nil
}
changes := []r53Types.Change{}
changeDesc := []string{} // TODO(tlim): This should be a [][]string so that we aren't joining strings until the last moment.
@ -700,13 +507,6 @@ func nativeToRecords(set r53Types.ResourceRecordSet, origin string) ([]*models.R
return results, nil
}
func getAliasMap(r *models.RecordConfig) map[string]string {
if r.Type != "R53_ALIAS" {
return nil
}
return r.R53Alias
}
func aliasToRRSet(zone r53Types.HostedZone, r *models.RecordConfig) *r53Types.ResourceRecordSet {
target := r.GetTargetField()
zoneID := getZoneID(zone, r)

View file

@ -5,7 +5,6 @@ import (
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/pkg/txtutil"
)
@ -35,17 +34,12 @@ func (api *rwthProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, exis
txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records
domain := dc.Name
var corrections []*models.Correction
var differ diff.Differ
if !diff2.EnableDiff2 {
differ = diff.New(dc)
} else {
differ = diff.NewCompat(dc)
}
_, create, del, modify, err := differ.IncrementalDiff(existingRecords)
toReport, create, del, modify, err := diff.NewCompat(dc).IncrementalDiff(existingRecords)
if err != nil {
return nil, err
}
// Start corrections with the reports
corrections := diff.GenerateMessageCorrections(toReport)
for _, d := range create {
des := d.Desired

View file

@ -8,7 +8,6 @@ import (
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/pkg/printer"
"github.com/StackExchange/dnscontrol/v4/providers"
"github.com/softlayer/softlayer-go/datatypes"
@ -82,16 +81,12 @@ func (s *softlayerProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, a
return nil, err
}
var corrections []*models.Correction
var create, deletes, modify diff.Changeset
if !diff2.EnableDiff2 {
_, create, deletes, modify, err = diff.New(dc).IncrementalDiff(actual)
} else {
_, create, deletes, modify, err = diff.NewCompat(dc).IncrementalDiff(actual)
}
toReport, create, deletes, modify, err := diff.NewCompat(dc).IncrementalDiff(actual)
if err != nil {
return nil, err
}
// Start corrections with the reports
corrections := diff.GenerateMessageCorrections(toReport)
for _, del := range deletes {
existing := del.Existing.Original.(datatypes.Dns_Domain_ResourceRecord)

View file

@ -7,7 +7,6 @@ import (
"strings"
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/providers"
"github.com/transip/gotransip/v6"
@ -102,16 +101,13 @@ func (n *transipProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, cur
removeOtherNS(dc)
if !diff2.EnableDiff2 {
corrections, err := n.getCorrectionsUsingOldDiff(dc, curRecords)
return corrections, err
}
corrections, err := n.getCorrectionsUsingDiff2(dc, curRecords)
return corrections, err
}
func (n *transipProvider) getCorrectionsUsingDiff2(dc *models.DomainConfig, records models.Records) ([]*models.Correction, error) {
var corrections []*models.Correction
instructions, err := diff2.ByRecordSet(records, dc, nil)
if err != nil {
return nil, err
@ -243,76 +239,6 @@ func canDirectApplyDNSEntries(change diff2.Change) bool {
return true
}
func (n *transipProvider) getCorrectionsUsingOldDiff(dc *models.DomainConfig, records models.Records) ([]*models.Correction, error) {
var corrections []*models.Correction
differ := diff.New(dc)
_, create, del, modify, err := differ.IncrementalDiff(records)
if err != nil {
return nil, err
}
for _, del := range del {
entry, err := recordToNative(del.Existing, true)
if err != nil {
return nil, err
}
corrections = append(corrections, &models.Correction{
Msg: del.String(),
F: func() error { return n.domains.RemoveDNSEntry(dc.Name, entry) },
})
}
for _, cre := range create {
entry, err := recordToNative(cre.Desired, false)
if err != nil {
return nil, err
}
corrections = append(corrections, &models.Correction{
Msg: cre.String(),
F: func() error { return n.domains.AddDNSEntry(dc.Name, entry) },
})
}
for _, mod := range modify {
targetEntry, err := recordToNative(mod.Desired, false)
if err != nil {
return nil, err
}
// TransIP identifies records by (Label, TTL Type), we can only update it if only the contents
// has changed. Otherwise we delete the old record and create the new one
if canUpdateDNSEntry(mod.Desired, mod.Existing) {
corrections = append(corrections, &models.Correction{
Msg: mod.String(),
F: func() error { return n.domains.UpdateDNSEntry(dc.Name, targetEntry) },
})
} else {
oldEntry, err := recordToNative(mod.Existing, true)
if err != nil {
return nil, err
}
corrections = append(corrections,
&models.Correction{
Msg: mod.String() + "[1/2]",
F: func() error { return n.domains.RemoveDNSEntry(dc.Name, oldEntry) },
},
&models.Correction{
Msg: mod.String() + "[2/2]",
F: func() error { return n.domains.AddDNSEntry(dc.Name, targetEntry) },
},
)
}
}
return corrections, nil
}
func canUpdateDNSEntry(desired *models.RecordConfig, existing *models.RecordConfig) bool {
return desired.Name == existing.Name && desired.TTL == existing.TTL && desired.Type == existing.Type
}

View file

@ -11,7 +11,6 @@ import (
"golang.org/x/oauth2"
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/providers"
"github.com/vultr/govultr/v2"
@ -111,6 +110,7 @@ func (api *vultrProvider) GetZoneRecords(domain string, meta map[string]string)
// GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records.
func (api *vultrProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, curRecords models.Records) ([]*models.Correction, error) {
var corrections []*models.Correction
for _, rec := range dc.Records {
switch rec.Type { // #rtype_variations
@ -126,48 +126,6 @@ func (api *vultrProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, cur
}
}
var corrections []*models.Correction
if !diff2.EnableDiff2 {
differ := diff.New(dc)
_, create, toDelete, modify, err := differ.IncrementalDiff(curRecords)
if err != nil {
return nil, err
}
for _, mod := range toDelete {
id := mod.Existing.Original.(govultr.DomainRecord).ID
corrections = append(corrections, &models.Correction{
Msg: fmt.Sprintf("%s; Vultr RecordID: %v", mod.String(), id),
F: func() error {
return api.client.DomainRecord.Delete(context.Background(), dc.Name, id)
},
})
}
for _, mod := range modify {
r := toVultrRecord(dc, mod.Desired, mod.Existing.Original.(govultr.DomainRecord).ID)
corrections = append(corrections, &models.Correction{
Msg: fmt.Sprintf("%s; Vultr RecordID: %v", mod.String(), r.ID),
F: func() error {
return api.client.DomainRecord.Update(context.Background(), dc.Name, r.ID, &govultr.DomainRecordReq{Name: r.Name, Type: r.Type, Data: r.Data, TTL: r.TTL, Priority: &r.Priority})
},
})
}
for _, mod := range create {
r := toVultrRecord(dc, mod.Desired, "0")
corrections = append(corrections, &models.Correction{
Msg: mod.String(),
F: func() error {
_, err := api.client.DomainRecord.Create(context.Background(), dc.Name, &govultr.DomainRecordReq{Name: r.Name, Type: r.Type, Data: r.Data, TTL: r.TTL, Priority: &r.Priority})
return err
},
})
}
return corrections, nil
}
changes, err := diff2.ByRecord(curRecords, dc, nil)
if err != nil {