mirror of
https://github.com/StackExchange/dnscontrol.git
synced 2024-09-20 06:46:19 +08:00
CHORE: Remove diff1 from codebase (#2575)
This commit is contained in:
parent
d3b358470d
commit
c91fe6c1c8
108
.github/workflows/pr_test.yml
vendored
108
.github/workflows/pr_test.yml
vendored
|
@ -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
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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),
|
||||
|
|
114
commands/types/dnscontrol.d.ts
vendored
114
commands/types/dnscontrol.d.ts
vendored
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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, "*")`.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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
|
||||
//}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
355
pkg/diff/diff.go
355
pkg/diff/diff.go
|
@ -1,14 +1,8 @@
|
|||
package diff
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
|
||||
"github.com/StackExchange/dnscontrol/v4/models"
|
||||
"github.com/StackExchange/dnscontrol/v4/pkg/printer"
|
||||
"github.com/fatih/color"
|
||||
"github.com/gobwas/glob"
|
||||
)
|
||||
|
||||
// Correlation stores a difference between two records.
|
||||
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -7,12 +7,6 @@
|
|||
"registrar": "none",
|
||||
"dnsProviders": {},
|
||||
"records": [],
|
||||
"ignored_names": [
|
||||
{
|
||||
"pattern": "\\*.testignore",
|
||||
"types": "*"
|
||||
}
|
||||
],
|
||||
"unmanaged": [
|
||||
{
|
||||
"label_pattern": "\\*.testignore",
|
||||
|
@ -22,4 +16,4 @@
|
|||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:"}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue