From e75c1ac9acc087144cc71b5d3c0cdc905365063e Mon Sep 17 00:00:00 2001 From: Tom Limoncelli Date: Tue, 21 Jan 2025 16:24:46 -0500 Subject: [PATCH] Squashed commit of the following: commit 901a3ac1c9ec93d504b7de48f0134ebb466a87f1 Author: Tom Limoncelli Date: Tue Jan 21 14:43:33 2025 -0500 CHORE: Update dependencies (#3397) commit 70e96590142bfd8084e874bf1666c8d3dd795a03 Author: Tom Limoncelli Date: Tue Jan 21 14:29:53 2025 -0500 MSDNS: Provider is failing due to lint fix gone wrong (#3396) commit 5e15bbe676332beaeae1a3838450b2d6f3e678f2 Author: Jakob Ackermann Date: Sat Jan 18 13:54:37 2025 +0000 BUG: fetch zones once in ZoneCache (#3394) commit a631c5bfdd5a46cbf4046232b3ad8858949c6794 Author: Kai Schwarz Date: Fri Jan 17 20:15:10 2025 +0100 CNR: Initial Performance improvement; golint review (#3391) commit e1c9785159df631a1991f5d8b2569e0d6b5c45a9 Author: Tom Limoncelli Date: Fri Jan 17 07:11:10 2025 -0500 CHORE: Update dependencies (#3385) commit 9e88b6a801e49260f1615469396cc8d9ed0371cc Author: Tom Limoncelli Date: Thu Jan 16 21:47:10 2025 -0500 CICD: Make pager tests more visible (#3387) commit 67db0e287da5818757e042a9cc4818db2fda2b4d Author: Tom Limoncelli Date: Thu Jan 16 14:41:18 2025 -0500 GCLOUD: remove (irrelevant) slow test (#3384) commit c348e354ff7f4ee6c996490e3236594e4b6f60a5 Author: Tom Limoncelli Date: Thu Jan 16 14:32:32 2025 -0500 GCLOUD: CICD: Skip the pager1201 integration test (#3383) commit 5cfb9073a20026294159d91f69156a52125515bc Author: Tom Limoncelli Date: Thu Jan 16 14:17:47 2025 -0500 TRANSIP: Pause when rate-limited (#3378) commit f666af8714267efe0b931b6f84565fea1341d919 Author: Tom Limoncelli Date: Thu Jan 16 13:56:46 2025 -0500 GCLOUD: Re-try on 502 errors (#3376) commit 1a1a4bf00d7d6402471525f4d0d8676e3b689ec1 Author: Tom Limoncelli Date: Thu Jan 16 12:54:48 2025 -0500 INWX: Enable SRV to have "." target (#3380) commit 355643988ef90ed71becb1e42f4f976b01e39a02 Author: Tom Limoncelli Date: Thu Jan 16 10:58:11 2025 -0500 CLOUDFLAREAPI: No longer treat TTL=300 as special (#3368) Co-authored-by: Sukka commit 89c65b6683f5752a5435ac2d9dfc66da5ccb76b8 Author: Tom Limoncelli Date: Thu Jan 16 10:03:00 2025 -0500 INWX: Permit "." target for SRV records (#3377) commit fc2c5069202fb8d2e1eb70aba5dde103afc16022 Author: Tom Limoncelli Date: Wed Jan 15 18:28:15 2025 -0500 CICD: Warn user if -provider does not match profiles.json:TYPE (#3375) commit 0d5b3c22b72db6a9e01630937409c5e45f82f8b3 Author: Jakob Ackermann Date: Wed Jan 15 22:43:24 2025 +0000 CLOUDFLARE: adopt ZoneCache (#3373) commit 2ef23621b5fbf0652a4eb5c7c86fba56444e3644 Author: Jakob Ackermann Date: Wed Jan 15 20:23:02 2025 +0000 HETZNER: adopt ZoneCache (#3372) commit ab00797f89b598a391a428e24f2a05332ed109a3 Author: Tom Hughes Date: Wed Jan 15 02:07:19 2025 +0000 FEATURE: Extend PTR magic handling to support RFC4183 names (#3364) commit 5c9b17039e05cbdb66d2aa954e6dbdfbc0b007c0 Author: Jakob Ackermann Date: Wed Jan 15 02:05:17 2025 +0000 FEAT: Add ZoneCache primitive (#3365) --- documentation/provider/cloudflareapi.md | 7 +- documentation/provider/cnr.md | 18 ++- go.mod | 72 +++++----- go.sum | 157 +++++++++++---------- integrationTest/integration_test.go | 39 ++--- pkg/powershell/shell.go | 2 +- pkg/transform/ptr.go | 68 ++++++++- pkg/transform/ptr_test.go | 17 +++ pkg/zoneCache/zoneCache.go | 87 ++++++++++++ providers/cloudflare/cloudflareProvider.go | 62 +++----- providers/cloudflare/rest.go | 37 +++-- providers/cnr/cnrProvider.go | 8 +- providers/cnr/domains.go | 41 ++---- providers/cnr/error.go | 4 +- providers/cnr/nameservers.go | 10 +- providers/cnr/records.go | 59 ++++---- providers/gcloud/gcloudProvider.go | 69 --------- providers/gcloud/retry.go | 94 ++++++++++++ providers/hetzner/api.go | 40 ++---- providers/hetzner/hetznerProvider.go | 33 ++--- providers/hetzner/types.go | 6 +- providers/inwx/auditrecords.go | 2 - providers/inwx/inwxProvider.go | 14 +- providers/transip/retry.go | 76 ++++++++++ providers/transip/transipProvider.go | 22 ++- 25 files changed, 633 insertions(+), 411 deletions(-) create mode 100644 pkg/zoneCache/zoneCache.go create mode 100644 providers/gcloud/retry.go create mode 100644 providers/transip/retry.go diff --git a/documentation/provider/cloudflareapi.md b/documentation/provider/cloudflareapi.md index 4c7e7160c7..a031b161d6 100644 --- a/documentation/provider/cloudflareapi.md +++ b/documentation/provider/cloudflareapi.md @@ -404,16 +404,15 @@ When `-cfworkers=false` is set, tests related to Workers are skipped. The Accou Cloudflare plays tricks with TTLs. Cloudflare uses "1" to mean "auto-ttl"; which as far as we can tell means 300 seconds (5 minutes) with the option that -CloudFlare may dynamically adjust the actual TTL. In the Cloudflare API, -setting the TTL to 300 results in the TTL being set to 1. +CloudFlare may dynamically adjust the actual TTL. If the TTL isn't set to 1, Cloudflare has a minimum of 1 minutes. A TTL of 0 tells DNSControl to use the default TTL for that provider, which is 1. In summary: -* TTL of 0, 1 and 300 are all the same ("auto TTL"). +* TTL of 0 and 1 are the same ("auto TTL"). * TTL of 2-60 are all the same as 60. -* TTL of 61-299, and 301 to infinity are not magic. +* TTL of 61 to infinity is not magic. Some of this is documented on the Cloudflare website's [Time to Live (TTL)](https://developers.cloudflare.com/dns/manage-dns-records/reference/ttl/) page. diff --git a/documentation/provider/cnr.md b/documentation/provider/cnr.md index c673819f2d..1869bb1f6c 100644 --- a/documentation/provider/cnr.md +++ b/documentation/provider/cnr.md @@ -19,7 +19,11 @@ Example: "apilogin": "your-cnr-account-id", "apipassword": "your-cnr-account-password", "apientity": "LIVE", // for the LIVE system; use "OTE" for the OT&E system - "debugmode": "0", // set it to "1" to get debug output of the communication with our Backend System API + // --- debugmode --- + // "0" -> turned off (default) + // "1" -> turned on, basic logging of the changes reflected as API command parameters for the CNR API + // "2" -> turned on, most verbose level - showing the detailed CNR API communication + "debugmode": "0" } } ``` @@ -46,23 +50,27 @@ Here a working example for our OT&E System: {% endhint %} With the above CentralNic Reseller entry in `creds.json`, you can run the -integration tests as follows: +integration tests or by specifying the data per environment vars as follows: -```shell -dnscontrol get-zones --format=nameonly cnr CNR all -``` ```shell # Review the output. Pick one domain and set CNR_DOMAIN. export CNR_DOMAIN=yodream.com # Pick a domain name. export CNR_ENTITY=OTE export CNR_UID=test.user export CNR_PW=test.passw0rd +export CNR_DEBUGMODE=2 cd integrationTest # NOTE: Not needed if already in that subdirectory go test -v -verbose -profile CNR ``` ## Usage +Fetch a list of all DNSZones: + +```shell +dnscontrol get-zones --format=nameonly cnr CNR all +``` + Here's an example DNS Configuration `dnsconfig.js` using our provider module. Even though it shows how you use us as Domain Registrar AND DNS Provider, we don't force you to do that. You are free to decide if you want to use both of our provider technology or just one of them. diff --git a/go.mod b/go.mod index 46f4d6e0ba..c53c75bb95 100644 --- a/go.mod +++ b/go.mod @@ -4,12 +4,12 @@ go 1.23.0 retract v4.8.0 -require google.golang.org/protobuf v1.35.2 // indirect +require google.golang.org/protobuf v1.36.2 // indirect -require golang.org/x/net v0.33.0 +require golang.org/x/net v0.34.0 require ( - github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0 github.com/Azure/go-autorest/autorest/to v0.4.0 @@ -18,16 +18,16 @@ require ( github.com/TomOnTime/utfutil v0.0.0-20230223141146-125e65197b36 github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 - github.com/aws/aws-sdk-go-v2 v1.32.7 - github.com/aws/aws-sdk-go-v2/config v1.28.7 - github.com/aws/aws-sdk-go-v2/credentials v1.17.48 - github.com/aws/aws-sdk-go-v2/service/route53 v1.46.4 - github.com/aws/aws-sdk-go-v2/service/route53domains v1.28.2 + github.com/aws/aws-sdk-go-v2 v1.33.0 + github.com/aws/aws-sdk-go-v2/config v1.29.1 + github.com/aws/aws-sdk-go-v2/credentials v1.17.54 + github.com/aws/aws-sdk-go-v2/service/route53 v1.48.2 + github.com/aws/aws-sdk-go-v2/service/route53domains v1.28.5 github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 github.com/billputer/go-namecheap v0.0.0-20210108011502-994a912fb7f9 github.com/centralnicgroup-opensource/rtldev-middleware-go-sdk/v4 v4.0.7 - github.com/cloudflare/cloudflare-go v0.113.0 - github.com/digitalocean/godo v1.132.0 + github.com/cloudflare/cloudflare-go v0.114.0 + github.com/digitalocean/godo v1.134.0 github.com/ditashi/jsbeautifier-go v0.0.0-20141206144643-2520a8026a9c github.com/dnsimple/dnsimple-go v1.7.0 github.com/exoscale/egoscale v0.102.4 @@ -51,51 +51,51 @@ require ( github.com/transip/gotransip/v6 v6.26.0 github.com/urfave/cli/v2 v2.27.5 github.com/xddxdd/ottoext v0.0.0-20221109171055-210517fa4419 - golang.org/x/crypto v0.31.0 // indirect + golang.org/x/crypto v0.32.0 // indirect golang.org/x/oauth2 v0.25.0 - google.golang.org/api v0.214.0 + google.golang.org/api v0.217.0 gopkg.in/ns1/ns1-go.v2 v2.13.0 ) require ( - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 github.com/G-Core/gcore-dns-sdk-go v0.2.9 - github.com/centralnicgroup-opensource/rtldev-middleware-go-sdk/v5 v5.0.4 + github.com/centralnicgroup-opensource/rtldev-middleware-go-sdk/v5 v5.0.6 github.com/containrrr/shoutrrr v0.8.0 github.com/fatih/color v1.18.0 github.com/fbiville/markdown-table-formatter v0.3.0 github.com/go-acme/lego/v4 v4.21.0 github.com/google/go-cmp v0.6.0 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 - github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.130 + github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.132 github.com/juju/errors v1.0.0 github.com/kylelemons/godebug v1.1.0 github.com/mattn/go-isatty v0.0.20 - github.com/oracle/oci-go-sdk/v65 v65.81.1 + github.com/oracle/oci-go-sdk/v65 v65.81.3 github.com/vultr/govultr/v2 v2.17.2 - golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329 + golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 golang.org/x/text v0.21.0 golang.org/x/time v0.9.0 gopkg.in/yaml.v3 v3.0.1 ) require ( - cloud.google.com/go/auth v0.13.0 // indirect - cloud.google.com/go/auth/oauth2adapt v0.2.6 // indirect + cloud.google.com/go/auth v0.14.0 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect cloud.google.com/go/compute/metadata v0.6.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect - github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 // indirect github.com/andybalholm/cascadia v1.3.3 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.24 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.28 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.28 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.24.8 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.33.3 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.9 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.24.11 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.10 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.33.9 // indirect github.com/aws/smithy-go v1.22.1 // indirect github.com/boombuler/barcode v1.0.1 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect @@ -113,10 +113,10 @@ require ( github.com/gofrs/uuid v4.4.0+incompatible // indirect github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/google/go-querystring v1.1.0 // indirect - github.com/google/s2a-go v0.1.8 // indirect + github.com/google/s2a-go v0.1.9 // indirect github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect - github.com/googleapis/gax-go/v2 v2.14.0 // indirect + github.com/googleapis/gax-go/v2 v2.14.1 // indirect github.com/gopherjs/gopherjs v1.17.2 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect @@ -152,15 +152,15 @@ require ( github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect go.mongodb.org/mongo-driver v1.12.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect - go.opentelemetry.io/otel v1.29.0 // indirect - go.opentelemetry.io/otel/metric v1.29.0 // indirect - go.opentelemetry.io/otel/trace v1.29.0 // indirect + go.opentelemetry.io/otel v1.31.0 // indirect + go.opentelemetry.io/otel/metric v1.31.0 // indirect + go.opentelemetry.io/otel/trace v1.31.0 // indirect golang.org/x/mod v0.22.0 // indirect golang.org/x/sync v0.10.0 // indirect - golang.org/x/sys v0.28.0 // indirect - golang.org/x/tools v0.28.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect - google.golang.org/grpc v1.67.1 // indirect + golang.org/x/sys v0.29.0 // indirect + golang.org/x/tools v0.29.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250106144421-5f5ef82da422 // indirect + google.golang.org/grpc v1.69.4 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/sourcemap.v1 v1.0.5 // indirect moul.io/http2curl v1.0.0 // indirect diff --git a/go.sum b/go.sum index b371fd5aa1..bd5c484623 100644 --- a/go.sum +++ b/go.sum @@ -1,17 +1,17 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go/auth v0.13.0 h1:8Fu8TZy167JkW8Tj3q7dIkr2v4cndv41ouecJx0PAHs= -cloud.google.com/go/auth v0.13.0/go.mod h1:COOjD9gwfKNKz+IIduatIhYJQIc0mG3H102r/EMxX6Q= -cloud.google.com/go/auth/oauth2adapt v0.2.6 h1:V6a6XDu2lTwPZWOawrAa9HUK+DB2zfJyTuciBG5hFkU= -cloud.google.com/go/auth/oauth2adapt v0.2.6/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8= +cloud.google.com/go/auth v0.14.0 h1:A5C4dKV/Spdvxcl0ggWwWEzzP7AZMJSEIgrkngwhGYM= +cloud.google.com/go/auth v0.14.0/go.mod h1:CYsoRL1PdiDuqeQpZE0bP2pnPrGqFcOkI0nldEQis+A= +cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M= +cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc= cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 h1:JZg6HRh6W6U4OLl6lk7BZ7BLisIzM9dG1R50zUk9C/M= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0/go.mod h1:YL1xnZ6QejvQHWJrX/AvhFl4WW4rqHVoKspWNVwFk0M= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 h1:B/dfvscEQtew9dVuoxqxrUKKv8Ih2f55PydknDamU+g= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0/go.mod h1:fiPSssYvltE08HJchL04dOy+RD4hgrjph0cwGGMntdI= -github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.0 h1:+m0M/LFxN43KvULkDNfdXOgrjtg6UYJPFBJyuEcRCAw= -github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.0/go.mod h1:PwOyop78lveYMRs6oCxjiVyBdyCgIYH6XHIVZO9/SFQ= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 h1:g0EZJwz7xkXQiZAI5xi9f3WWFYBlX1CPTrR+NDToRkQ= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0/go.mod h1:XCW7KnZet0Opnr7HccfUw1PLc4CjHqpcaxW8DHklNkQ= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1 h1:1mvYtZfWQAnwNah/C+Z+Jb9rQH95LPE2vlmMuWAHJk8= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1/go.mod h1:75I/mXtme1JyWFtz8GocPHVFyH421IBoZErnO16dd0k= +github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.1 h1:Bk5uOhSAenHyR5P61D/NzeQCv+4fEVV8mOkJ82NqpWw= +github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.1/go.mod h1:QZ4pw3or1WPmRBxf0cHd1tknzrT54WPBOQoGutCPvSU= github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY= github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 h1:lpOxwrQ919lCZoNCd69rVt8u1eLZuMORrGXqy8sNf3c= @@ -28,8 +28,8 @@ github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+X github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM= github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= -github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU= -github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= +github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 h1:kYRSnvJju5gYVyhkij+RTJ/VR6QIUaCfWeaFm2ycsjQ= +github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/DisposaBoy/JsonConfigReader v0.0.0-20201129172854-99cf318d67e7 h1:AJKJCKcb/psppPl/9CUiQQnTG+Bce0/cIweD5w5Q7aQ= github.com/DisposaBoy/JsonConfigReader v0.0.0-20201129172854-99cf318d67e7/go.mod h1:GCzqZQHydohgVLSIqRKZeTt8IGb1Y4NaFfim3H40uUI= @@ -46,34 +46,34 @@ github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM= github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/aws/aws-sdk-go-v2 v1.32.7 h1:ky5o35oENWi0JYWUZkB7WYvVPP+bcRF5/Iq7JWSb5Rw= -github.com/aws/aws-sdk-go-v2 v1.32.7/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U= -github.com/aws/aws-sdk-go-v2/config v1.28.7 h1:GduUnoTXlhkgnxTD93g1nv4tVPILbdNQOzav+Wpg7AE= -github.com/aws/aws-sdk-go-v2/config v1.28.7/go.mod h1:vZGX6GVkIE8uECSUHB6MWAUsd4ZcG2Yq/dMa4refR3M= -github.com/aws/aws-sdk-go-v2/credentials v1.17.48 h1:IYdLD1qTJ0zanRavulofmqut4afs45mOWEI+MzZtTfQ= -github.com/aws/aws-sdk-go-v2/credentials v1.17.48/go.mod h1:tOscxHN3CGmuX9idQ3+qbkzrjVIx32lqDSU1/0d/qXs= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22 h1:kqOrpojG71DxJm/KDPO+Z/y1phm1JlC8/iT+5XRmAn8= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22/go.mod h1:NtSFajXVVL8TA2QNngagVZmUtXciyrHOt7xgz4faS/M= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26 h1:I/5wmGMffY4happ8NOCuIUEWGUvvFp5NSeQcXl9RHcI= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26/go.mod h1:FR8f4turZtNy6baO0KJ5FJUmXH/cSkI9fOngs0yl6mA= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26 h1:zXFLuEuMMUOvEARXFUVJdfqZ4bvvSgdGRq/ATcrQxzM= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26/go.mod h1:3o2Wpy0bogG1kyOPrgkXA8pgIfEEv0+m19O9D5+W8y8= +github.com/aws/aws-sdk-go-v2 v1.33.0 h1:Evgm4DI9imD81V0WwD+TN4DCwjUMdc94TrduMLbgZJs= +github.com/aws/aws-sdk-go-v2 v1.33.0/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U= +github.com/aws/aws-sdk-go-v2/config v1.29.1 h1:JZhGawAyZ/EuJeBtbQYnaoftczcb2drR2Iq36Wgz4sQ= +github.com/aws/aws-sdk-go-v2/config v1.29.1/go.mod h1:7bR2YD5euaxBhzt2y/oDkt3uNRb6tjFp98GlTFueRwk= +github.com/aws/aws-sdk-go-v2/credentials v1.17.54 h1:4UmqeOqJPvdvASZWrKlhzpRahAulBfyTJQUaYy4+hEI= +github.com/aws/aws-sdk-go-v2/credentials v1.17.54/go.mod h1:RTdfo0P0hbbTxIhmQrOsC/PquBZGabEPnCaxxKRPSnI= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.24 h1:5grmdTdMsovn9kPZPI23Hhvp0ZyNm5cRO+IZFIYiAfw= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.24/go.mod h1:zqi7TVKTswH3Ozq28PkmBmgzG1tona7mo9G2IJg4Cis= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.28 h1:igORFSiH3bfq4lxKFkTSYDhJEUCYo6C8VKiWJjYwQuQ= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.28/go.mod h1:3So8EA/aAYm36L7XIvCVwLa0s5N0P7o2b1oqnx/2R4g= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.28 h1:1mOW9zAUMhTSrMDssEHS/ajx8JcAj/IcftzcmNlmVLI= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.28/go.mod h1:kGlXVIWDfvt2Ox5zEaNglmq0hXPHgQFNMix33Tw22jA= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 h1:8eUsivBQzZHqe/3FE+cqwfH+0p5Jo8PFM/QYQSmeZ+M= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7/go.mod h1:kLPQvGUmxn/fqiCrDeohwG33bq2pQpGeY62yRO6Nrh0= -github.com/aws/aws-sdk-go-v2/service/route53 v1.46.4 h1:0jMtawybbfpFEIMy4wvfyW2Z4YLr7mnuzT0fhR67Nrc= -github.com/aws/aws-sdk-go-v2/service/route53 v1.46.4/go.mod h1:xlMODgumb0Pp8bzfpojqelDrf8SL9rb5ovwmwKJl+oU= -github.com/aws/aws-sdk-go-v2/service/route53domains v1.28.2 h1:IH+UWySP8buYbojqBKwOGOZN6zpAc4sd8gTDLEJYl74= -github.com/aws/aws-sdk-go-v2/service/route53domains v1.28.2/go.mod h1:kbxY8XfDPZYpcBqQtezB8r1DqZWgCydOSmgoKcTjvf4= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.8 h1:CvuUmnXI7ebaUAhbJcDy9YQx8wHR69eZ9I7q5hszt/g= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.8/go.mod h1:XDeGv1opzwm8ubxddF0cgqkZWsyOtw4lr6dxwmb6YQg= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7 h1:F2rBfNAL5UyswqoeWv9zs74N/NanhK16ydHW1pahX6E= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7/go.mod h1:JfyQ0g2JG8+Krq0EuZNnRwX0mU0HrwY/tG6JNfcqh4k= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.3 h1:Xgv/hyNgvLda/M9l9qxXc4UFSgppnRczLxlMs5Ae/QY= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.3/go.mod h1:5Gn+d+VaaRgsjewpMvGazt0WfcFO+Md4wLOuBfGR9Bc= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.9 h1:TQmKDyETFGiXVhZfQ/I0cCFziqqX58pi4tKJGYGFSz0= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.9/go.mod h1:HVLPK2iHQBUx7HfZeOQSEu3v2ubZaAY2YPbAm5/WUyY= +github.com/aws/aws-sdk-go-v2/service/route53 v1.48.2 h1:Rxg1R0CHxVb9ggQLufOkr4an3yFEkTDN+N5+LFU4aEg= +github.com/aws/aws-sdk-go-v2/service/route53 v1.48.2/go.mod h1:TN4PcCL0lvqmYcv+AV8iZFC4Sd0FM06QDaoBXrFEftU= +github.com/aws/aws-sdk-go-v2/service/route53domains v1.28.5 h1:AsaMJNlWKQcfHllLuR6di0Zom1j4oU6IDsjGbKDVYtI= +github.com/aws/aws-sdk-go-v2/service/route53domains v1.28.5/go.mod h1:wVo1eEn3UNZY8HeErLpqTaPpsTW8W/EJrFXk+MezmKo= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.11 h1:kuIyu4fTT38Kj7YCC7ouNbVZSSpqkZ+LzIfhCr6Dg+I= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.11/go.mod h1:Ro744S4fKiCCuZECXgOi760TiYylUM8ZBf6OGiZzJtY= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.10 h1:l+dgv/64iVlQ3WsBbnn+JSbkj01jIi+SM0wYsj3y/hY= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.10/go.mod h1:Fzsj6lZEb8AkTE5S68OhcbBqeWPsR8RnGuKPr8Todl8= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.9 h1:BRVDbewN6VZcwr+FBOszDKvYeXY1kJ+GGMCcpghlw0U= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.9/go.mod h1:f6vjfZER1M17Fokn0IzssOTMT2N8ZSq+7jnNF0tArvw= github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro= github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0= @@ -89,15 +89,15 @@ github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyY github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/centralnicgroup-opensource/rtldev-middleware-go-sdk/v4 v4.0.7 h1:Jk7uhY5q11fE5PlEupX2Lo12w82UhGC6bE1CI5jwFbc= github.com/centralnicgroup-opensource/rtldev-middleware-go-sdk/v4 v4.0.7/go.mod h1:FnQtD0+Q/1NZxi0eEWN+3ZRyMsE9vzSB3YjyunkbKD0= -github.com/centralnicgroup-opensource/rtldev-middleware-go-sdk/v5 v5.0.4 h1:vdTRJeoZmyPalQZ+FYQc1CWyJgzL/xl36VB6snMKCWA= -github.com/centralnicgroup-opensource/rtldev-middleware-go-sdk/v5 v5.0.4/go.mod h1:5HfPOmFFOS8VVC/BJnOZmOA7wY05h9h+LpqebcsTWSs= +github.com/centralnicgroup-opensource/rtldev-middleware-go-sdk/v5 v5.0.6 h1:SFPF26qTjwoHK5FoRgMeX2vl5Ju5OWxQ6RbYWpaLQlk= +github.com/centralnicgroup-opensource/rtldev-middleware-go-sdk/v5 v5.0.6/go.mod h1:LHBakdE+gwmBd27bDvL4+QmOY13O0GqdQj6D2R2b078= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/cloudflare-go v0.113.0 h1:qnOXmA6RbgZ4rg5gNBK5QGk0Pzbv8pnUYV3C4+8CU6w= -github.com/cloudflare/cloudflare-go v0.113.0/go.mod h1:Dlm4BAnycHc0i8yLxQZb9b+OlMwYOAoDJsUOEFgpVvo= +github.com/cloudflare/cloudflare-go v0.114.0 h1:ucoti4/7Exo0XQ+rzpn1H+IfVVe++zgiM+tyKtf0HUA= +github.com/cloudflare/cloudflare-go v0.114.0/go.mod h1:O7fYfFfA6wKqKFn2QIR9lhj7FDw6VQCGOY6hd2TBtd0= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/containrrr/shoutrrr v0.8.0 h1:mfG2ATzIS7NR2Ec6XL+xyoHzN97H8WPjir8aYzJUSec= github.com/containrrr/shoutrrr v0.8.0/go.mod h1:ioyQAyu1LJY6sILuNyKaQaw+9Ttik5QePU8atnAdO2o= @@ -115,8 +115,8 @@ github.com/deepmap/oapi-codegen v1.9.1 h1:yHmEnA7jSTUMQgV+uN02WpZtwHnz2CBW3mZRIx github.com/deepmap/oapi-codegen v1.9.1/go.mod h1:PLqNAhdedP8ttRpBBkzLKU3bp+Fpy+tTgeAMlztR2cw= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/digitalocean/godo v1.132.0 h1:n0x6+ZkwbyQBtIU1wwBhv26EINqHg0wWQiBXlwYg/HQ= -github.com/digitalocean/godo v1.132.0/go.mod h1:PU8JB6I1XYkQIdHFop8lLAY9ojp6M0XcU0TWaQSxbrc= +github.com/digitalocean/godo v1.134.0 h1:dT7aQR9jxNOQEZwzP+tAYcxlj5szFZScC33+PAYGQVM= +github.com/digitalocean/godo v1.134.0/go.mod h1:PU8JB6I1XYkQIdHFop8lLAY9ojp6M0XcU0TWaQSxbrc= github.com/ditashi/jsbeautifier-go v0.0.0-20141206144643-2520a8026a9c h1:+Zo5Ca9GH0RoeVZQKzFJcTLoAixx5s5Gq3pTIS+n354= github.com/ditashi/jsbeautifier-go v0.0.0-20141206144643-2520a8026a9c/go.mod h1:HJGU9ULdREjOcVGZVPB5s6zYmHi1RxzT71l2wQyLmnE= github.com/dnsimple/dnsimple-go v1.7.0 h1:JKu9xJtZ3SqOC+BuYgAWeab7+EEx0sz422vu8j611ZY= @@ -208,8 +208,8 @@ github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM= -github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA= +github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= +github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -217,8 +217,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw= github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA= -github.com/googleapis/gax-go/v2 v2.14.0 h1:f+jMrjBPl+DL9nI4IQzLUxMq7XrAqFYB7hBPqMNIe8o= -github.com/googleapis/gax-go/v2 v2.14.0/go.mod h1:lhBCnjdLrWRaPvLWhmc8IS24m9mr07qSYnHncrgo+zk= +github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q= +github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= @@ -252,8 +252,8 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/vault/api v1.15.0 h1:O24FYQCWwhwKnF7CuSqP30S51rTV7vz1iACXE/pj5DA= github.com/hashicorp/vault/api v1.15.0/go.mod h1:+5YTO09JGn0u+b6ySD/LLVf8WkJCPLAL2Vkmrn2+CM8= -github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.130 h1:Gt6VLu7EyDaCZF8+YP1arqqZajSgsJZQiCBNMjSx1qE= -github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.130/go.mod h1:JWz2ujO9X3oU5wb6kXp+DpR2UuDj2SldDbX8T0FSuhI= +github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.132 h1:5LqzrJa8LADcY0sDEdV35e8nbwI7RoUQEt+KXWvWoY0= +github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.132/go.mod h1:JWz2ujO9X3oU5wb6kXp+DpR2UuDj2SldDbX8T0FSuhI= github.com/jarcoal/httpmock v1.3.0 h1:2RJ8GP0IIaWwcC9Fp2BmVi8Kog3v2Hn7VXM3fTd+nuc= github.com/jarcoal/httpmock v1.3.0/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg= github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= @@ -339,8 +339,8 @@ github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM= github.com/onsi/gomega v1.31.1 h1:KYppCUK+bUgAZwHOu7EXVBKyQA6ILvOESHkn/tgoqvo= github.com/onsi/gomega v1.31.1/go.mod h1:y40C95dwAD1Nz36SsEnxvfFe8FFfNxzI5eJ0EYGyAy0= -github.com/oracle/oci-go-sdk/v65 v65.81.1 h1:JYc47bk8n/MUchA2KHu1ggsCQzlJZQLJ+tTKfOho00E= -github.com/oracle/oci-go-sdk/v65 v65.81.1/go.mod h1:IBEV9l1qBzUpo7zgGaRUhbB05BVfcDGYRFBCPlTcPp0= +github.com/oracle/oci-go-sdk/v65 v65.81.3 h1:L4JcHSV4xLxySfZOQumUazlRN/2u/7r7Muw0Apg7UYI= +github.com/oracle/oci-go-sdk/v65 v65.81.3/go.mod h1:IBEV9l1qBzUpo7zgGaRUhbB05BVfcDGYRFBCPlTcPp0= github.com/ovh/go-ovh v1.6.0 h1:ixLOwxQdzYDx296sXcgS35TOPEahJkpjMGtzPadCjQI= github.com/ovh/go-ovh v1.6.0/go.mod h1:cTVDnl94z4tl8pP1uZ/8jlVxntjSIf09bNcQ5TJSC7c= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= @@ -363,8 +363,8 @@ github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/qdm12/reprint v0.0.0-20200326205758-722754a53494 h1:wSmWgpuccqS2IOfmYrbRiUgv+g37W5suLLLxwwniTSc= github.com/qdm12/reprint v0.0.0-20200326205758-722754a53494/go.mod h1:yipyliwI08eQ6XwDm1fEwKPdF/xdbkiHtrU+1Hg+vc4= -github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4= -github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA= +github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E= +github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= github.com/robertkrimen/otto v0.0.0-20200922221731-ef014fd054ac/go.mod h1:xvqspoSXJTIpemEonrMDFq6XzwHYYgToXWj5eRX1OtY= github.com/robertkrimen/otto v0.5.1 h1:avDI4ToRk8k1hppLdYFTuuzND41n37vPGJU7547dGf0= github.com/robertkrimen/otto v0.5.1/go.mod h1:bS433I4Q9p+E5pZLu7r17vP6FkE6/wLxBdmKjoqJXF8= @@ -444,12 +444,16 @@ go.mongodb.org/mongo-driver v1.12.0 h1:aPx33jmn/rQuJXPQLZQ8NtfPQG8CaqgLThFtqRb0P go.mongodb.org/mongo-driver v1.12.0/go.mod h1:AZkxhPnFJUoH7kZlFkVKucV20K387miPfm7oimrSmK0= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8= -go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw= -go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8= -go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc= -go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8= -go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4= -go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= +go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY= +go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE= +go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE= +go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY= +go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk= +go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0= +go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc= +go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8= +go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys= +go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -462,11 +466,12 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= +golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329 h1:9kj3STMvgqy3YA4VQXBrN7925ICMxD5wzMRcgA30588= -golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= +golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA= +golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -497,8 +502,9 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= @@ -541,8 +547,9 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -583,29 +590,29 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= -golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= +golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= +golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.214.0 h1:h2Gkq07OYi6kusGOaT/9rnNljuXmqPnaig7WGPmKbwA= -google.golang.org/api v0.214.0/go.mod h1:bYPpLG8AyeMWwDU6NXoB00xC0DFkikVvd5MfwoxjLqE= +google.golang.org/api v0.217.0 h1:GYrUtD289o4zl1AhiTZL0jvQGa2RDLyC+kX1N/lfGOU= +google.golang.org/api v0.217.0/go.mod h1:qMc2E8cBAbQlRypBTBWHklNJlaZZJBwDv81B1Iu8oSI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38 h1:Q3nlH8iSQSRUwOskjbcSMcF2jiYMNiQYZ0c2KEJLKKU= -google.golang.org/genproto/googleapis/api v0.0.0-20241118233622-e639e219e697 h1:pgr/4QbFyktUv9CtQ/Fq4gzEE6/Xs7iCXbktaGzLHbQ= -google.golang.org/genproto/googleapis/api v0.0.0-20241118233622-e639e219e697/go.mod h1:+D9ySVjN8nY8YCVjc5O7PZDIdZporIDY3KaGfJunh88= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 h1:8ZmaLZE4XWrtU3MyClkYqqtl6Oegr3235h7jxsDyqCY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= +google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q= +google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250106144421-5f5ef82da422 h1:3UsHvIr4Wc2aW4brOaSCmcxh9ksica6fHEr8P1XhkYw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250106144421-5f5ef82da422/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= -google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= +google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A= +google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -615,8 +622,8 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= -google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.2 h1:R8FeyR1/eLmkutZOM5CWghmo5itiG9z0ktFlTVLuTmU= +google.golang.org/protobuf v1.36.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/integrationTest/integration_test.go b/integrationTest/integration_test.go index dbf7081369..8065adb471 100644 --- a/integrationTest/integration_test.go +++ b/integrationTest/integration_test.go @@ -100,8 +100,12 @@ func getProvider(t *testing.T) (providers.DNSServiceProvider, string, map[string if *profileFlag == "" { *profileFlag = profileName } + // Fill in -provider if blank. + if *providerFlag == "" { + *providerFlag = profileType + } - // Fix legacy use of -provider. + // Sanity check. If the user-specifed -provider flag doesn't match what was in the file, warn them. if *providerFlag != profileType { if *providerFlag != "" { fmt.Printf("WARNING: -provider=%q does not match profile TYPE=%q. Using profile TYPE.\n", *providerFlag, profileType) @@ -110,7 +114,7 @@ func getProvider(t *testing.T) (providers.DNSServiceProvider, string, map[string } // fmt.Printf("DEBUG flag=%q Profile=%q TYPE=%q\n", *providerFlag, profileName, profileType) - fmt.Printf("Testing Profile=%q TYPE=%q\n", profileName, profileType) + fmt.Printf("Testing Profile=%q (TYPE=%q)\n", profileName, profileType) var metadata json.RawMessage @@ -1475,9 +1479,9 @@ func makeTests() []*TestGroup { "TRANSIP", // Doesn't page. Works fine. Due to the slow API we skip. "CNR", // Test beaks limits. ), - tc("99 records", manyA("rec%04d", "1.2.3.4", 99)...), - tc("100 records", manyA("rec%04d", "1.2.3.4", 100)...), - tc("101 records", manyA("rec%04d", "1.2.3.4", 101)...), + tc("99 records", manyA("pager101-rec%04d", "1.2.3.4", 99)...), + tc("100 records", manyA("pager101-rec%04d", "1.2.3.4", 100)...), + tc("101 records", manyA("pager101-rec%04d", "1.2.3.4", 101)...), ), testgroup("pager601", @@ -1488,12 +1492,12 @@ func makeTests() []*TestGroup { //"DESEC", // Skip due to daily update limits. //"GANDI_V5", // Their API is so damn slow. We'll add it back as needed. //"MSDNS", // No paging done. No need to test. - "GCLOUD", + //"GCLOUD", //"HEXONET", // Doesn't page. Works fine. Due to the slow API we skip. "ROUTE53", // Batches up changes in pages. ), - tc("601 records", manyA("rec%04d", "1.2.3.4", 600)...), - tc("Update 601 records", manyA("rec%04d", "1.2.3.5", 600)...), + tc("601 records", manyA("pager601-rec%04d", "1.2.3.4", 600)...), + tc("Update 601 records", manyA("pager601-rec%04d", "1.2.3.5", 600)...), ), testgroup("pager1201", @@ -1506,13 +1510,13 @@ func makeTests() []*TestGroup { //"GANDI_V5", // Their API is so damn slow. We'll add it back as needed. //"HEDNS", // No paging done. No need to test. //"MSDNS", // No paging done. No need to test. - "GCLOUD", + //"GCLOUD", //"HEXONET", // Doesn't page. Works fine. Due to the slow API we skip. "HOSTINGDE", // Pages. "ROUTE53", // Batches up changes in pages. ), - tc("1200 records", manyA("rec%04d", "1.2.3.4", 1200)...), - tc("Update 1200 records", manyA("rec%04d", "1.2.3.5", 1200)...), + tc("1200 records", manyA("pager1201-rec%04d", "1.2.3.4", 1200)...), + tc("Update 1200 records", manyA("pager1201-rec%04d", "1.2.3.5", 1200)...), ), // Test the boundaries of Google' batch system. @@ -1520,16 +1524,17 @@ func makeTests() []*TestGroup { // https://github.com/StackExchange/dnscontrol/pull/2762#issuecomment-1877825559 testgroup("batchRecordswithOthers", only( - "GCLOUD", + //"GCLOUD", + "HOSTINGDE", // Pages. ), tc("1200 records", - manyA("rec%04d", "1.2.3.4", 1200)...), + manyA("batch-rec%04d", "1.2.3.4", 1200)...), tc("Update 1200 records and Create others", append( - manyA("arec%04d", "1.2.3.4", 1200), - manyA("rec%04d", "1.2.3.5", 1200)...)...), + manyA("batch-arec%04d", "1.2.3.4", 1200), + manyA("batch-rec%04d", "1.2.3.5", 1200)...)...), tc("Update 1200 records and Create and Delete others", append( - manyA("rec%04d", "1.2.3.4", 1200), - manyA("zrec%04d", "1.2.3.4", 1200)...)...), + manyA("batch-rec%04d", "1.2.3.4", 1200), + manyA("batch-zrec%04d", "1.2.3.4", 1200)...)...), ), //// CanUse* types: diff --git a/pkg/powershell/shell.go b/pkg/powershell/shell.go index d4a8131a74..455f488f8f 100644 --- a/pkg/powershell/shell.go +++ b/pkg/powershell/shell.go @@ -60,7 +60,7 @@ func (s *shell) Execute(cmd string) (string, string, error) { waiter.Add(2) go streamReader(s.stdout, outBoundary, &sout, waiter) //nolint:errcheck - go streamReader(s.stdout, outBoundary, &sout, waiter) //nolint:errcheck + go streamReader(s.stderr, errBoundary, &serr, waiter) //nolint:errcheck waiter.Wait() diff --git a/pkg/transform/ptr.go b/pkg/transform/ptr.go index 53b71d9bcb..4ec0c81cad 100644 --- a/pkg/transform/ptr.go +++ b/pkg/transform/ptr.go @@ -51,18 +51,22 @@ func ipv4magic(name, domain string) (string, error) { if strings.HasSuffix(rev, "."+domain) { return result, nil } - if ipMatchesClasslessDomain(ip, domain) { - return strings.SplitN(rev, ".", 2)[0], nil + octets := ipMatchesClasslessDomain(ip, domain) + if octets > 0 { + return strings.Join(strings.SplitN(rev, ".", octets+1)[0:octets], "."), nil } return "", fmt.Errorf("PTR record %v in wrong IPv4 domain (%v)", name, domain) } var isRfc2317Format1 = regexp.MustCompile(`(\d{1,3})/(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.in-addr\.arpa$`) +var isRfc4183Format1 = regexp.MustCompile(`(\d{1,3})-(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.in-addr\.arpa$`) +var isRfc4183Format2 = regexp.MustCompile(`(\d{1,3})-(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.in-addr\.arpa$`) +var isRfc4183Format3 = regexp.MustCompile(`(\d{1,3})-(\d{1,3})\.(\d{1,3})\.in-addr\.arpa$`) // ipMatchesClasslessDomain returns true if ip is appropriate for domain. // domain is a reverse DNS lookup zone (in-addr.arpa) as described in RFC2317. -func ipMatchesClasslessDomain(ip net.IP, domain string) bool { +func ipMatchesClasslessDomain(ip net.IP, domain string) int { // The unofficial but preferred format in RFC2317: m := isRfc2317Format1.FindStringSubmatch(domain) if m != nil { @@ -77,13 +81,67 @@ func ipMatchesClasslessDomain(ip net.IP, domain string) bool { f, m, x, y, z := atob(m[1]), atob(m[2]), atob(m[3]), atob(m[4]), atob(m[5]) masked := ip.Mask(net.CIDRMask(int(m), 32)) if a == z && b == y && c == x && masked.Equal(net.IPv4(a, b, c, f)) { - return true + return 1 + } + } + + // The format in RFC4183 for /25 to /32: + m = isRfc4183Format1.FindStringSubmatch(domain) + if m != nil { + // IP: Domain: + // 172.20.18.27 128-27.18.20.172.in-addr.arpa + // A B C D F M X Y Z + // The following should be true: + // A==Z, B==Y, C==X. + // If you mask ip by M, the last octet should be F. + ii := ip.To4() + a, b, c, _ := ii[0], ii[1], ii[2], ii[3] + f, m, x, y, z := atob(m[1]), atob(m[2]), atob(m[3]), atob(m[4]), atob(m[5]) + masked := ip.Mask(net.CIDRMask(int(m), 32)) + if a == z && b == y && c == x && masked.Equal(net.IPv4(a, b, c, f)) { + return 1 + } + } + + // The format in RFC4183 for /17 to /23: + m = isRfc4183Format2.FindStringSubmatch(domain) + if m != nil { + // IP: Domain: + // 172.20.18.27 128-27.20.172.in-addr.arpa + // A B C D F M Y Z + // The following should be true: + // A==Z, B==Y, + // If you mask ip by M, the second last octet should be F. + ii := ip.To4() + a, b, _, _ := ii[0], ii[1], ii[2], ii[3] + f, m, y, z := atob(m[1]), atob(m[2]), atob(m[3]), atob(m[4]) + masked := ip.Mask(net.CIDRMask(int(m), 32)) + if a == z && b == y && masked.Equal(net.IPv4(a, b, f, 0)) { + return 2 + } + } + + // The format in RFC4183 for /9 to /15: + m = isRfc4183Format3.FindStringSubmatch(domain) + if m != nil { + // IP: Domain: + // 172.20.18.27 128-27.172.in-addr.arpa + // A B C D F M Z + // The following should be true: + // A==Z, + // If you mask ip by M, the third last octet should be F. + ii := ip.To4() + a, _, _, _ := ii[0], ii[1], ii[2], ii[3] + f, m, z := atob(m[1]), atob(m[2]), atob(m[3]) + masked := ip.Mask(net.CIDRMask(int(m), 32)) + if a == z && masked.Equal(net.IPv4(a, f, 0, 0)) { + return 3 } } // To extend this to include other formats, add them here. - return false + return 0 } // atob converts a to a byte value or panics. diff --git a/pkg/transform/ptr_test.go b/pkg/transform/ptr_test.go index 93a1d3a916..dee42dc08f 100644 --- a/pkg/transform/ptr_test.go +++ b/pkg/transform/ptr_test.go @@ -48,6 +48,23 @@ func TestPtrMagic(t *testing.T) { {"172.20.18.191", "160/27.18.20.172.in-addr.arpa", "191", false}, {"172.20.18.192", "160/27.18.20.172.in-addr.arpa", "", true}, + // RFC4183 (Classless) + // 172.20.18.160/27 is .160 - .191: + {"172.20.18.159", "160-27.18.20.172.in-addr.arpa", "", true}, + {"172.20.18.160", "160-27.18.20.172.in-addr.arpa", "160", false}, + {"172.20.18.191", "160-27.18.20.172.in-addr.arpa", "191", false}, + {"172.20.18.192", "160-27.18.20.172.in-addr.arpa", "", true}, + // 172.20.160.0/19 is .160 - .191: + {"172.20.159.1", "160-19.20.172.in-addr.arpa", "", true}, + {"172.20.160.2", "160-19.20.172.in-addr.arpa", "2.160", false}, + {"172.20.191.3", "160-19.20.172.in-addr.arpa", "3.191", false}, + {"172.20.192.4", "160-19.20.172.in-addr.arpa", "", true}, + // 172.160.0.0/11 is .160 - .191: + {"172.159.1.5", "160-11.172.in-addr.arpa", "", true}, + {"172.160.2.6", "160-11.172.in-addr.arpa", "6.2.160", false}, + {"172.191.3.7", "160-11.172.in-addr.arpa", "7.3.191", false}, + {"172.192.4.8", "160-11.172.in-addr.arpa", "", true}, + // If it doesn't end in .arpa, the magic is disabled: {"1.2.3.4", "example.com", "1.2.3.4", false}, {"1", "example.com", "1", false}, diff --git a/pkg/zoneCache/zoneCache.go b/pkg/zoneCache/zoneCache.go new file mode 100644 index 0000000000..9823d01059 --- /dev/null +++ b/pkg/zoneCache/zoneCache.go @@ -0,0 +1,87 @@ +package zoneCache + +import ( + "errors" + "sync" +) + +func New[Zone any](fetchAll func() (map[string]Zone, error)) ZoneCache[Zone] { + return ZoneCache[Zone]{fetchAll: fetchAll} +} + +var ErrZoneNotFound = errors.New("zone not found") + +type ZoneCache[Zone any] struct { + mu sync.Mutex + cached bool + cache map[string]Zone + fetchAll func() (map[string]Zone, error) +} + +func (c *ZoneCache[Zone]) ensureCached() error { + if c.cached { + return nil + } + zones, err := c.fetchAll() + if err != nil { + return err + } + if c.cache == nil { + c.cache = make(map[string]Zone, len(zones)) + } + for name, z := range zones { + c.cache[name] = z + } + c.cached = true + return nil +} + +func (c *ZoneCache[Zone]) HasZone(name string) (bool, error) { + c.mu.Lock() + defer c.mu.Unlock() + + if err := c.ensureCached(); err != nil { + return false, err + } + _, ok := c.cache[name] + return ok, nil +} + +func (c *ZoneCache[Zone]) GetZone(name string) (Zone, error) { + c.mu.Lock() + defer c.mu.Unlock() + + if err := c.ensureCached(); err != nil { + var z Zone + return z, err + } + z, ok := c.cache[name] + if !ok { + return z, ErrZoneNotFound + } + return z, nil +} + +func (c *ZoneCache[Zone]) GetZoneNames() ([]string, error) { + c.mu.Lock() + defer c.mu.Unlock() + + if err := c.ensureCached(); err != nil { + return nil, err + } + names := make([]string, 0, len(c.cache)) + for name := range c.cache { + names = append(names, name) + } + return names, nil +} + +func (c *ZoneCache[Zone]) SetZone(name string, z Zone) { + c.mu.Lock() + defer c.mu.Unlock() + + if c.cache == nil { + c.cache = make(map[string]Zone, 1) + } + c.cache[name] = z +} diff --git a/providers/cloudflare/cloudflareProvider.go b/providers/cloudflare/cloudflareProvider.go index e74df6b059..3c447ff8d8 100644 --- a/providers/cloudflare/cloudflareProvider.go +++ b/providers/cloudflare/cloudflareProvider.go @@ -8,12 +8,12 @@ import ( "os" "strconv" "strings" - "sync" "github.com/StackExchange/dnscontrol/v4/models" "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/pkg/printer" "github.com/StackExchange/dnscontrol/v4/pkg/transform" + "github.com/StackExchange/dnscontrol/v4/pkg/zoneCache" "github.com/StackExchange/dnscontrol/v4/providers" "github.com/cloudflare/cloudflare-go" "github.com/fatih/color" @@ -92,39 +92,21 @@ type cloudflareProvider struct { tcLogFh *os.File // Transcode Log file handle tcZone string // Transcode Current zone - sync.Mutex // Protects all access to the following fields: - domainIndex map[string]string // Cache of zone name to zone ID. - nameservers map[string][]string // Cache of zone name to list of nameservers. + zoneCache zoneCache.ZoneCache[cloudflare.Zone] } // GetNameservers returns the nameservers for a domain. func (c *cloudflareProvider) GetNameservers(domain string) ([]*models.Nameserver, error) { - c.Lock() - defer c.Unlock() - if err := c.cacheDomainList(); err != nil { + z, err := c.zoneCache.GetZone(domain) + if err != nil { return nil, err } - - ns, ok := c.nameservers[domain] - if !ok { - return nil, fmt.Errorf("nameservers for %s not found in cloudflare cache(%q)", domain, c.accountID) - } - return models.ToNameservers(ns) + return models.ToNameservers(z.NameServers) } // ListZones returns a list of the DNS zones. func (c *cloudflareProvider) ListZones() ([]string, error) { - c.Lock() - defer c.Unlock() - if err := c.cacheDomainList(); err != nil { - return nil, err - } - - zones := make([]string, 0, len(c.domainIndex)) - for d := range c.domainIndex { - zones = append(zones, d) - } - return zones, nil + return c.zoneCache.GetZoneNames() } // GetZoneRecords gets the records of a zone and returns them in RecordConfig format. @@ -185,17 +167,11 @@ func (c *cloudflareProvider) GetZoneRecords(domain string, meta map[string]strin } func (c *cloudflareProvider) getDomainID(name string) (string, error) { - c.Lock() - defer c.Unlock() - if err := c.cacheDomainList(); err != nil { + z, err := c.zoneCache.GetZone(name) + if err != nil { return "", err } - - id, ok := c.domainIndex[name] - if !ok { - return "", fmt.Errorf("'%s' not a zone in cloudflare account", name) - } - return id, nil + return z.ID, nil } // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. @@ -496,9 +472,9 @@ func (c *cloudflareProvider) preprocessConfig(dc *models.DomainConfig) error { rec.Metadata = map[string]string{} } // cloudflare uses "1" to mean "auto-ttl" - // if we get here and ttl is not specified (or is the dnscontrol default of 300), + // if we get here and ttl is not specified // use automatic mode instead. - if rec.TTL == 0 || rec.TTL == 300 { + if rec.TTL == 0 { rec.TTL = 1 } if rec.TTL != 1 && rec.TTL < 60 { @@ -657,6 +633,7 @@ func (c *cloudflareProvider) LogTranscode(zone string, redirect *models.CFSINGLE func newCloudflare(m map[string]string, metadata json.RawMessage) (providers.DNSServiceProvider, error) { api := &cloudflareProvider{} + api.zoneCache = zoneCache.New(api.fetchAllZones) // check api keys from creds json file if m["apitoken"] == "" && (m["apikey"] == "" || m["apiuser"] == "") { return nil, errors.New("if cloudflare apitoken is not set, apikey and apiuser must be provided") @@ -912,20 +889,15 @@ func getProxyMetadata(r *models.RecordConfig) map[string]string { // EnsureZoneExists creates a zone if it does not exist func (c *cloudflareProvider) EnsureZoneExists(domain string) error { - c.Lock() - defer c.Unlock() - if err := c.cacheDomainList(); err != nil { + if ok, err := c.zoneCache.HasZone(domain); err != nil || ok { return err } - - if _, ok := c.domainIndex[domain]; ok { - return nil - } - var id string id, err := c.createZone(domain) + if err != nil { + return err + } printer.Printf("Added zone for %s to Cloudflare account: %s\n", domain, id) - clear(c.domainIndex) // clear the cache so that the next caller has to refresh it, thus loading the new ID. - return err + return nil } // PrepareCloudflareTestWorkers creates Cloudflare Workers required for CF_WORKER_ROUTE integration tests. diff --git a/providers/cloudflare/rest.go b/providers/cloudflare/rest.go index 15c0e4ce52..f64664087b 100644 --- a/providers/cloudflare/rest.go +++ b/providers/cloudflare/rest.go @@ -11,38 +11,26 @@ import ( "golang.org/x/net/idna" ) -// get list of domains for account. Cache so the ids can be looked up from domain name -// The caller must do all locking. -func (c *cloudflareProvider) cacheDomainList() error { - if c.domainIndex != nil { - return nil - } - - // fmt.Printf("DEBUG: CLOUDFLARE POPULATING CACHE\n") +func (c *cloudflareProvider) fetchAllZones() (map[string]cloudflare.Zone, error) { zones, err := c.cfClient.ListZones(context.Background()) if err != nil { - return fmt.Errorf("failed fetching domain list from cloudflare(%q): %w", c.cfClient.APIEmail, err) + return nil, fmt.Errorf("failed fetching domain list from cloudflare(%q): %w", c.cfClient.APIEmail, err) } - c.domainIndex = map[string]string{} - c.nameservers = map[string][]string{} - + m := make(map[string]cloudflare.Zone, len(zones)) for _, zone := range zones { if encoded, err := idna.ToASCII(zone.Name); err == nil && encoded != zone.Name { - if _, ok := c.domainIndex[encoded]; ok { + if _, ok := m[encoded]; ok { fmt.Printf("WARNING: Zone %q appears twice in this cloudflare account\n", encoded) } - c.domainIndex[encoded] = zone.ID - c.nameservers[encoded] = zone.NameServers + m[encoded] = zone } - if _, ok := c.domainIndex[zone.Name]; ok { + if _, ok := m[zone.Name]; ok { fmt.Printf("WARNING: Zone %q appears twice in this cloudflare account\n", zone.Name) } - c.domainIndex[zone.Name] = zone.ID - c.nameservers[zone.Name] = zone.NameServers + m[zone.Name] = zone } - - return nil + return m, nil } // get all records for a domain @@ -68,7 +56,14 @@ func (c *cloudflareProvider) deleteDNSRecord(rec cloudflare.DNSRecord, domainID 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 + if err != nil { + return "", err + } + if encoded, err := idna.ToASCII(zone.Name); err == nil && encoded != zone.Name { + c.zoneCache.SetZone(encoded, zone) + } + c.zoneCache.SetZone(domainName, zone) + return zone.ID, nil } func cfDnskeyData(rec *models.RecordConfig) *cfRecData { diff --git a/providers/cnr/cnrProvider.go b/providers/cnr/cnrProvider.go index 63a339870f..c0eb1cf85d 100644 --- a/providers/cnr/cnrProvider.go +++ b/providers/cnr/cnrProvider.go @@ -15,8 +15,8 @@ var ( version = "dev" ) -// CNRClient describes a connection to the CNR API. -type CNRClient struct { +// Client describes a connection to the CNR API. +type Client struct { conf map[string]string APILogin string APIPassword string @@ -56,8 +56,8 @@ var features = providers.DocumentationNotes{ providers.CanUseTLSA: providers.Can(), } -func newProvider(conf map[string]string) (*CNRClient, error) { - api := &CNRClient{ +func newProvider(conf map[string]string) (*Client, error) { + api := &Client{ conf: conf, client: cnrcl.NewAPIClient(), } diff --git a/providers/cnr/domains.go b/providers/cnr/domains.go index 7991536c05..99645a1c5e 100644 --- a/providers/cnr/domains.go +++ b/providers/cnr/domains.go @@ -3,36 +3,25 @@ package cnr // EnsureZoneExists returns an error // * if access to dnszone is not allowed (not authorized) or // * if it doesn't exist and creating it fails -func (n *CNRClient) EnsureZoneExists(domain string) error { - r := n.client.Request(map[string]interface{}{ - "COMMAND": "StatusDNSZone", +func (n *Client) EnsureZoneExists(domain string) error { + command := map[string]interface{}{ + "COMMAND": "AddDNSZone", "DNSZONE": domain, - }) - code := r.GetCode() - if code == 545 { - command := map[string]interface{}{ - "COMMAND": "AddDNSZone", - "DNSZONE": domain, - } - if n.APIEntity == "OTE" { - command["SOATTL"] = "33200" - command["SOASERIAL"] = "0000000000" - } - // Create the zone - r = n.client.Request(command) - if !r.IsSuccess() { - return n.GetCNRApiError("Failed to create not existing zone ", domain, r) - } - } else if code == 531 { - return n.GetCNRApiError("Not authorized to manage dnszone", domain, r) - } else if r.IsError() || r.IsTmpError() { - return n.GetCNRApiError("Error while checking status of dnszone", domain, r) } - return nil + if n.APIEntity == "OTE" { + command["SOATTL"] = "33200" + command["SOASERIAL"] = "0000000000" + } + // Create the zone + r := n.client.Request(command) + if r.GetCode() == 549 || r.IsSuccess() { + return nil + } + return n.GetAPIError("Failed to create not existing zone ", domain, r) } // ListZones lists all the -func (n *CNRClient) ListZones() ([]string, error) { +func (n *Client) ListZones() ([]string, error) { var zones []string // Basic @@ -42,7 +31,7 @@ func (n *CNRClient) ListZones() ([]string, error) { }) for _, r := range rs { if r.IsError() { - return nil, n.GetCNRApiError("Error while QueryDNSZoneList", "Basic", &r) + return nil, n.GetAPIError("Error while QueryDNSZoneList", "Basic", &r) } zoneColumn := r.GetColumn("DNSZONE") if zoneColumn != nil { diff --git a/providers/cnr/error.go b/providers/cnr/error.go index a51c6b0045..0febda1f75 100644 --- a/providers/cnr/error.go +++ b/providers/cnr/error.go @@ -6,7 +6,7 @@ import ( "github.com/centralnicgroup-opensource/rtldev-middleware-go-sdk/v5/response" ) -// GetCNRApiError returns an error including API error code and error description. -func (n *CNRClient) GetCNRApiError(format string, objectid string, r *response.Response) error { +// GetAPIError returns an error including API error code and error description. +func (n *Client) GetAPIError(format string, objectid string, r *response.Response) error { return fmt.Errorf(format+" %q. [%v %s]", objectid, r.GetCode(), r.GetDescription()) } diff --git a/providers/cnr/nameservers.go b/providers/cnr/nameservers.go index d2f7e000ba..540808d62d 100644 --- a/providers/cnr/nameservers.go +++ b/providers/cnr/nameservers.go @@ -18,7 +18,7 @@ var defaultNameservers = []*models.Nameserver{ var nsRegex = regexp.MustCompile(`ns([1-3]{1})[0-9]+\.rrpproxy\.net`) // GetNameservers gets the nameservers set on a domain. -func (n *CNRClient) GetNameservers(domain string) ([]*models.Nameserver, error) { +func (n *Client) GetNameservers(domain string) ([]*models.Nameserver, error) { // NOTE: This information is taken over from HX and adapted to CNR... might be wrong... // This is an interesting edge case. CNR expects you to SET the nameservers to ns[1-3].rrpproxy.net, // but it will internally set it to (ns1xyz|ns2uvw|ns3asd).rrpproxy.net, where xyz/uvw/asd is a uniqueish number. @@ -41,14 +41,14 @@ func (n *CNRClient) GetNameservers(domain string) ([]*models.Nameserver, error) return models.ToNameservers(toUse) } -func (n *CNRClient) getNameserversRaw(domain string) ([]string, error) { +func (n *Client) getNameserversRaw(domain string) ([]string, error) { r := n.client.Request(map[string]interface{}{ "COMMAND": "StatusDomain", "DOMAIN": domain, }) code := r.GetCode() if code != 200 { - return nil, n.GetCNRApiError("Could not get status for domain", domain, r) + return nil, n.GetAPIError("Could not get status for domain", domain, r) } nsColumn := r.GetColumn("NAMESERVER") if nsColumn == nil { @@ -61,7 +61,7 @@ func (n *CNRClient) getNameserversRaw(domain string) ([]string, error) { } // GetRegistrarCorrections gathers corrections that would being n to match dc. -func (n *CNRClient) GetRegistrarCorrections(dc *models.DomainConfig) ([]*models.Correction, error) { +func (n *Client) GetRegistrarCorrections(dc *models.DomainConfig) ([]*models.Correction, error) { nss, err := n.getNameserversRaw(dc.Name) if err != nil { return nil, err @@ -87,7 +87,7 @@ func (n *CNRClient) GetRegistrarCorrections(dc *models.DomainConfig) ([]*models. return nil, nil } -func (n *CNRClient) updateNameservers(ns []string, domain string) func() error { +func (n *Client) updateNameservers(ns []string, domain string) func() error { return func() error { cmd := map[string]interface{}{ "COMMAND": "ModifyDomain", diff --git a/providers/cnr/records.go b/providers/cnr/records.go index 0dd2d556f1..1c9772df8e 100644 --- a/providers/cnr/records.go +++ b/providers/cnr/records.go @@ -14,8 +14,8 @@ import ( "github.com/StackExchange/dnscontrol/v4/pkg/txtutil" ) -// CNRRecord covers an individual DNS resource record. -type CNRRecord struct { +// Record covers an individual DNS resource record. +type Record struct { // DomainName is the zone that the record belongs to. DomainName string // Host is the hostname relative to the zone: e.g. for a record for blog.example.org, domain would be "example.org" and host would be "blog". @@ -36,7 +36,7 @@ type CNRRecord struct { } // GetZoneRecords gets the records of a zone and returns them in RecordConfig format. -func (n *CNRClient) GetZoneRecords(domain string, meta map[string]string) (models.Records, error) { +func (n *Client) GetZoneRecords(domain string, meta map[string]string) (models.Records, error) { records, err := n.getRecords(domain) if err != nil { return nil, err @@ -50,7 +50,7 @@ func (n *CNRClient) GetZoneRecords(domain string, meta map[string]string) (model } // GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records. -func (n *CNRClient) GetZoneRecordsCorrections(dc *models.DomainConfig, actual models.Records) ([]*models.Correction, int, error) { +func (n *Client) GetZoneRecordsCorrections(dc *models.DomainConfig, actual models.Records) ([]*models.Correction, int, error) { toReport, create, del, mod, actualChangeCount, err := diff.NewCompat(dc).IncrementalDiff(actual) if err != nil { return nil, 0, err @@ -82,7 +82,7 @@ func (n *CNRClient) GetZoneRecordsCorrections(dc *models.DomainConfig, actual mo changes = true fmt.Fprintln(buf, d) key := fmt.Sprintf("DELRR%d", delrridx) - oldRecordString := n.deleteRecordString(d.Existing.Original.(*CNRRecord)) + oldRecordString := n.deleteRecordString(d.Existing.Original.(*Record)) params[key] = oldRecordString fmt.Fprintf(&builder, "\033[31m- %s = %s\033[0m\n", key, oldRecordString) delrridx++ @@ -92,7 +92,7 @@ func (n *CNRClient) GetZoneRecordsCorrections(dc *models.DomainConfig, actual mo fmt.Fprintln(buf, chng) // old record deletion key := fmt.Sprintf("DELRR%d", delrridx) - oldRecordString := n.deleteRecordString(chng.Existing.Original.(*CNRRecord)) + oldRecordString := n.deleteRecordString(chng.Existing.Original.(*Record)) params[key] = oldRecordString fmt.Fprintf(&builder, "\033[31m- %s = %s\033[0m\n", key, oldRecordString) delrridx++ @@ -123,7 +123,7 @@ func (n *CNRClient) GetZoneRecordsCorrections(dc *models.DomainConfig, actual mo return corrections, actualChangeCount, nil } -func toRecord(r *CNRRecord, origin string) *models.RecordConfig { +func toRecord(r *Record, origin string) *models.RecordConfig { rc := &models.RecordConfig{ Type: r.Type, TTL: r.TTL, @@ -159,7 +159,7 @@ func toRecord(r *CNRRecord, origin string) *models.RecordConfig { } // updateZoneBy updates the zone with the provided changes. -func (n *CNRClient) updateZoneBy(params map[string]interface{}, domain string) error { +func (n *Client) updateZoneBy(params map[string]interface{}, domain string) error { zone := domain cmd := map[string]interface{}{ "COMMAND": "ModifyDNSZone", @@ -170,14 +170,14 @@ func (n *CNRClient) updateZoneBy(params map[string]interface{}, domain string) e } r := n.client.Request(cmd) if !r.IsSuccess() { - return n.GetCNRApiError("Error while updating zone", zone, r) + return n.GetAPIError("Error while updating zone", zone, r) } return nil } -// deleteRecordString constructs the record string based on the provided CNRRecord. -func (n *CNRClient) getRecords(domain string) ([]*CNRRecord, error) { - var records []*CNRRecord +// deleteRecordString constructs the record string based on the provided Record. +func (n *Client) getRecords(domain string) ([]*Record, error) { + var records []*Record // Command to find out the total numbers of resource records for the zone // so that the follow-up query can be done with the correct limit @@ -186,7 +186,8 @@ func (n *CNRClient) getRecords(domain string) ([]*CNRRecord, error) { "DNSZONE": domain, "ORDERBY": "type", "FIRST": "0", - "LIMIT": "1", + "LIMIT": "10000", + "WIDE": "1", } r := n.client.Request(cmd) @@ -201,29 +202,16 @@ func (n *CNRClient) getRecords(domain string) ([]*CNRRecord, error) { } } else { // Return specific error if the zone does not exist - return nil, n.GetCNRApiError("Use `dnscontrol create-domains` to create not-existing zone", domain, r) + return nil, n.GetAPIError("Use `dnscontrol create-domains` to create not-existing zone", domain, r) } } // Return general error for any other issues - return nil, n.GetCNRApiError("Failed loading resource records for zone", domain, r) + return nil, n.GetAPIError("Failed loading resource records for zone", domain, r) } totalRecords := r.GetRecordsTotalCount() if totalRecords <= 0 { return nil, nil } - limitation := 100 - totalRecords += limitation - - // finally request all resource records available for the zone - cmd["LIMIT"] = strconv.Itoa(totalRecords) - cmd["WIDE"] = "1" - r = n.client.Request(cmd) - - // Check if the request was successful - if !r.IsSuccess() { - // Return general error for any other issues - return nil, n.GetCNRApiError("Failed loading resource records for zone", domain, r) - } // loop over the records array rrs := r.GetRecords() @@ -265,8 +253,8 @@ func (n *CNRClient) getRecords(domain string) ([]*CNRRecord, error) { fqdn = fmt.Sprintf("%s.%s.", data["NAME"], domain) } - // Initialize a new CNRRecord - record := &CNRRecord{ + // Initialize a new Record + record := &Record{ DomainName: domain, Host: data["NAME"], Fqdn: fqdn, @@ -286,7 +274,7 @@ func (n *CNRClient) getRecords(domain string) ([]*CNRRecord, error) { } // Function to create record string from given RecordConfig for the ADDRR# API parameter -func (n *CNRClient) createRecordString(rc *models.RecordConfig, domain string) (string, error) { +func (n *Client) createRecordString(rc *models.RecordConfig, domain string) (string, error) { host := rc.GetLabel() answer := "" @@ -339,8 +327,8 @@ func (n *CNRClient) createRecordString(rc *models.RecordConfig, domain string) ( return str, nil } -// deleteRecordString constructs the record string based on the provided CNRRecord. -func (n *CNRClient) deleteRecordString(record *CNRRecord) string { +// deleteRecordString constructs the record string based on the provided Record. +func (n *Client) deleteRecordString(record *Record) string { // Initialize values slice values := []string{ record.Host, @@ -375,6 +363,7 @@ func isNoPopulate() bool { } // Function to check if debug mode is enabled -func (n *CNRClient) isDebugOn() bool { - return n.conf["debugmode"] == "1" || n.conf["debugmode"] == "2" +func (n *Client) isDebugOn() bool { + debugMode, exists := n.conf["debugmode"] + return exists && (debugMode == "1" || debugMode == "2") } diff --git a/providers/gcloud/gcloudProvider.go b/providers/gcloud/gcloudProvider.go index 27a7212653..9aeda07548 100644 --- a/providers/gcloud/gcloudProvider.go +++ b/providers/gcloud/gcloudProvider.go @@ -5,10 +5,8 @@ import ( "encoding/json" "errors" "fmt" - "log" "regexp" "strings" - "time" "github.com/StackExchange/dnscontrol/v4/models" "github.com/StackExchange/dnscontrol/v4/pkg/diff2" @@ -488,70 +486,3 @@ func (g *gcloudProvider) EnsureZoneExists(domain string) error { g.zones[domain+"."], err = g.client.ManagedZones.Create(g.project, mz).Do() return err } - -const ( - initialBackoff = time.Second * 10 // First delay duration - maxBackoff = time.Minute * 3 // Maximum backoff delay -) - -// backoff is the amount of time to sleep if a 429 or 504 is received. -// It is doubled after each use. -var ( - backoff = initialBackoff - backoff404 = false // Set if the last call requested a retry of a 404 -) - -func retryNeeded(resp *googleapi.ServerResponse, err error) bool { - if err != nil { - return false // Not an error. - } - serr, ok := err.(*googleapi.Error) - if !ok { - return false // Not a google error. - } - if serr.Code == 200 { - backoff = initialBackoff // Reset - return false // Success! No need to retry. - } - - if serr.Code == 404 { - // serr.Code == 404 happens occasionally when GCLOUD hasn't - // finished updating the database yet. We pause and retry - // exactly once. There should be a better way to do this, such as - // a callback that would tell us a transaction is complete. - if backoff404 { - backoff404 = false - return false // Give up. We've done this already. - } - log.Printf("Special 404 pause-and-retry for GCLOUD: Pausing %s\n", backoff) - time.Sleep(backoff) - backoff404 = true - return true // Request a retry. - } - backoff404 = false - - if serr.Code != 429 && serr.Code != 502 && serr.Code != 503 { - return false // Not an error that permits retrying. - } - - // TODO(tlim): In theory, resp.Header has a header that says how - // long to wait but I haven't been able to capture that header in - // the wild. If you get these "RUNCHANGE HEAD" messages, please - // file a bug with the contents! - - if resp != nil { - log.Printf("NOTE: If you see this message, please file a bug with the output below:\n") - log.Printf("RUNCHANGE CODE = %+v\n", resp.HTTPStatusCode) - log.Printf("RUNCHANGE HEAD = %+v\n", resp.Header) - } - - // a simple exponential back-off - log.Printf("Pausing due to ratelimit: %v seconds\n", backoff) - time.Sleep(backoff) - backoff = backoff + (backoff / 2) - if backoff > maxBackoff { - backoff = maxBackoff - } - - return true // Request the API call be re-tried. -} diff --git a/providers/gcloud/retry.go b/providers/gcloud/retry.go new file mode 100644 index 0000000000..0df9b88e1f --- /dev/null +++ b/providers/gcloud/retry.go @@ -0,0 +1,94 @@ +package gcloud + +import ( + "log" + "time" + + "google.golang.org/api/googleapi" +) + +const ( + initialBackoff = time.Second * 10 // First delay duration + maxBackoff = time.Minute * 3 // Maximum backoff delay +) + +// backoff is the amount of time to sleep if a 429 or 504 is received. +// It is doubled after each use. +var ( + backoff = initialBackoff + backoff404 = false // Set if the last call requested a retry of a 404 + backoff502 = false // Set if the last call requested a retry of a 502 +) + +func retryNeeded(resp *googleapi.ServerResponse, err error) bool { + if err == nil { + return false // Not an error. + } + + serr, ok := err.(*googleapi.Error) + if !ok { + return false // Not a google error. + } + + if serr.Code == 200 { + backoff = initialBackoff // Reset + return false // Success! No need to retry. + } + + if serr.Code == 404 { + // serr.Code == 404 happens occasionally when GCLOUD hasn't + // finished updating the database yet. We pause and retry + // exactly once. There should be a better way to do this, such as + // a callback that would tell us a transaction is complete. + if backoff404 { + backoff404 = false + return false // Give up. We've done this already. + } + log.Printf("Special 404 pause-and-retry for GCLOUD: Pausing %s\n", backoff) + time.Sleep(backoff) + backoff404 = true + return true // Request a retry. + } + backoff404 = false + + if serr.Code == 502 { + // serr.Code == 502 happens occasionally when "The server + // encountered a temporary error and could not complete your + // request. Please try again in 30 seconds. That’s all we know." + // We pause and retry exactly once. + if backoff502 { + backoff502 = false + return false // Give up. We've done this already. + } + log.Printf("Special 502 pause-and-retry for GCLOUD: Pausing %s\n", backoff) + time.Sleep(31 * time.Second) + backoff502 = true + return true // Request a retry. + } + backoff502 = false + + if serr.Code != 429 && serr.Code != 503 { + return false // Not an error that permits retrying. + } + + // TODO(tlim): In theory, resp.Header has a header that says how + // long to wait but I haven't been able to capture that header in + // the wild. If you get these "RUNCHANGE HEAD" messages, please + // file a bug with the contents! + + if resp != nil { + log.Printf("NOTE: If you see this message, please file a bug with the output below:\n") + log.Printf("RUNCHANGE CODE = %+v\n", resp.HTTPStatusCode) + log.Printf("RUNCHANGE HEAD = %+v\n", resp.Header) + } + + // a simple exponential back-off + log.Printf("Pausing due to ratelimit: %v seconds\n", backoff) + time.Sleep(backoff) + backoff = backoff + (backoff / 2) + if backoff > maxBackoff { + backoff = maxBackoff + } + + return true // Request the API call be re-tried. +} diff --git a/providers/hetzner/api.go b/providers/hetzner/api.go index ce5039ceb5..02f6470bf4 100644 --- a/providers/hetzner/api.go +++ b/providers/hetzner/api.go @@ -11,6 +11,7 @@ import ( "time" "github.com/StackExchange/dnscontrol/v4/pkg/printer" + "github.com/StackExchange/dnscontrol/v4/pkg/zoneCache" ) const ( @@ -19,8 +20,7 @@ const ( type hetznerProvider struct { apiKey string - mu sync.Mutex - cachedZones map[string]zone + zoneCache zoneCache.ZoneCache[zone] requestRateLimiter requestRateLimiter } @@ -62,7 +62,13 @@ func (api *hetznerProvider) createZone(name string) error { request := createZoneRequest{ Name: name, } - return api.request("/zones", "POST", request, nil, nil) + response := createZoneResponse{} + err := api.request("/zones", "POST", request, &response, nil) + if err != nil { + return err + } + api.zoneCache.SetZone(name, response.Zone) + return nil } func (api *hetznerProvider) deleteRecord(record *record) error { @@ -71,7 +77,7 @@ func (api *hetznerProvider) deleteRecord(record *record) error { } func (api *hetznerProvider) getAllRecords(domain string) ([]record, error) { - z, err := api.getZone(domain) + z, err := api.zoneCache.GetZone(domain) if err != nil { return nil, err } @@ -105,18 +111,7 @@ func (api *hetznerProvider) getAllRecords(domain string) ([]record, error) { return records, nil } -func (api *hetznerProvider) resetZoneCache() { - api.mu.Lock() - defer api.mu.Unlock() - api.cachedZones = nil -} - -func (api *hetznerProvider) getAllZones() (map[string]zone, error) { - api.mu.Lock() - defer api.mu.Unlock() - if api.cachedZones != nil { - return api.cachedZones, nil - } +func (api *hetznerProvider) fetchAllZones() (map[string]zone, error) { var zones map[string]zone page := 1 statusOK := func(code int) bool { @@ -148,22 +143,9 @@ func (api *hetznerProvider) getAllZones() (map[string]zone, error) { } page++ } - api.cachedZones = zones return zones, nil } -func (api *hetznerProvider) getZone(name string) (*zone, error) { - zones, err := api.getAllZones() - if err != nil { - return nil, err - } - z, ok := zones[name] - if !ok { - return nil, fmt.Errorf("%q is not a zone in this HETZNER account", name) - } - return &z, nil -} - func (api *hetznerProvider) request(endpoint string, method string, request interface{}, target interface{}, statusOK func(code int) bool) error { if statusOK == nil { statusOK = func(code int) bool { diff --git a/providers/hetzner/hetznerProvider.go b/providers/hetzner/hetznerProvider.go index b92db3f5bd..62b443105c 100644 --- a/providers/hetzner/hetznerProvider.go +++ b/providers/hetzner/hetznerProvider.go @@ -7,6 +7,7 @@ import ( "github.com/StackExchange/dnscontrol/v4/models" "github.com/StackExchange/dnscontrol/v4/pkg/diff" + "github.com/StackExchange/dnscontrol/v4/pkg/zoneCache" "github.com/StackExchange/dnscontrol/v4/providers" ) @@ -50,28 +51,20 @@ func New(settings map[string]string, _ json.RawMessage) (providers.DNSServicePro return nil, errors.New("missing HETZNER api_key") } - return &hetznerProvider{ - apiKey: apiKey, - }, nil + api := &hetznerProvider{apiKey: apiKey} + api.zoneCache = zoneCache.New(api.fetchAllZones) + return api, nil } // EnsureZoneExists creates a zone if it does not exist func (api *hetznerProvider) EnsureZoneExists(domain string) error { - domains, err := api.ListZones() - if err != nil { + if ok, err := api.zoneCache.HasZone(domain); err != nil || ok { return err } - for _, d := range domains { - if d == domain { - return nil - } - } - - if err = api.createZone(domain); err != nil { + if err := api.createZone(domain); err != nil { return err } - api.resetZoneCache() return nil } @@ -86,7 +79,7 @@ func (api *hetznerProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, e // Start corrections with the reports corrections := diff.GenerateMessageCorrections(toReport) - z, err := api.getZone(domain) + z, err := api.zoneCache.GetZone(domain) if err != nil { return nil, 0, err } @@ -143,7 +136,7 @@ func (api *hetznerProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, e // GetNameservers returns the nameservers for a domain. func (api *hetznerProvider) GetNameservers(domain string) ([]*models.Nameserver, error) { - z, err := api.getZone(domain) + z, err := api.zoneCache.GetZone(domain) if err != nil { return nil, err } @@ -169,13 +162,5 @@ func (api *hetznerProvider) GetZoneRecords(domain string, meta map[string]string // ListZones lists the zones on this account. func (api *hetznerProvider) ListZones() ([]string, error) { - zones, err := api.getAllZones() - if err != nil { - return nil, err - } - domains := make([]string, 0, len(zones)) - for domain := range zones { - domains = append(domains, domain) - } - return domains, nil + return api.zoneCache.GetZoneNames() } diff --git a/providers/hetzner/types.go b/providers/hetzner/types.go index 61125a5d99..40b17ad436 100644 --- a/providers/hetzner/types.go +++ b/providers/hetzner/types.go @@ -17,6 +17,10 @@ type createZoneRequest struct { Name string `json:"name"` } +type createZoneResponse struct { + Zone zone `json:"zone"` +} + type getAllRecordsResponse struct { Records []record `json:"records"` Meta struct { @@ -53,7 +57,7 @@ type zone struct { TTL uint32 `json:"ttl"` } -func fromRecordConfig(in *models.RecordConfig, zone *zone) record { +func fromRecordConfig(in *models.RecordConfig, zone zone) record { r := record{ Name: in.GetLabel(), Type: in.Type, diff --git a/providers/inwx/auditrecords.go b/providers/inwx/auditrecords.go index 64dff42e24..7a2853d195 100644 --- a/providers/inwx/auditrecords.go +++ b/providers/inwx/auditrecords.go @@ -11,8 +11,6 @@ import ( func AuditRecords(records []*models.RecordConfig) []error { a := rejectif.Auditor{} - a.Add("SRV", rejectif.SrvHasNullTarget) // Last verified 2020-12-28 - a.Add("TXT", rejectif.TxtHasBackticks) // Last verified 2021-03-01 a.Add("TXT", rejectif.TxtHasTrailingSpace) // Last verified 2021-03-01 diff --git a/providers/inwx/inwxProvider.go b/providers/inwx/inwxProvider.go index 3c20115330..b25fb71151 100644 --- a/providers/inwx/inwxProvider.go +++ b/providers/inwx/inwxProvider.go @@ -54,7 +54,7 @@ var features = providers.DocumentationNotes{ providers.CanUseLOC: providers.Unimplemented(), providers.CanUseNAPTR: providers.Can(), providers.CanUsePTR: providers.Can("PTR records with empty targets are not supported"), - providers.CanUseSRV: providers.Can("SRV records with empty targets are not supported."), + providers.CanUseSRV: providers.Can(), providers.CanUseSSHFP: providers.Can(), providers.CanUseSVCB: providers.Can(), providers.CanUseTLSA: providers.Can(), @@ -183,7 +183,7 @@ func makeNameserverRecordRequest(domain string, rec *models.RecordConfig) *goinw The API will not accept any target with a final dot but will instead always add this final dot internally. Records with empty targets (i.e. records with target ".") - are not allowed. + are allowed. */ case "CNAME", "NS": req.Content = content[:len(content)-1] @@ -196,7 +196,11 @@ func makeNameserverRecordRequest(domain string, rec *models.RecordConfig) *goinw } case "SRV": req.Priority = int(rec.SrvPriority) - req.Content = fmt.Sprintf("%d %d %v", rec.SrvWeight, rec.SrvPort, content[:len(content)-1]) + if content == "." { + req.Content = fmt.Sprintf("%d %d %v", rec.SrvWeight, rec.SrvPort, content) + } else { + req.Content = fmt.Sprintf("%d %d %v", rec.SrvWeight, rec.SrvPort, content[:len(content)-1]) + } default: req.Content = rec.GetTargetCombined() } @@ -308,7 +312,7 @@ func (api *inwxAPI) GetZoneRecords(domain string, meta map[string]string) (model The API will not accept any target with a final dot but will instead always add this final dot internally. Records with empty targets (i.e. records with target ".") - are not allowed. + are allowed. */ rtypeAddDot := map[string]bool{ "CNAME": true, @@ -320,6 +324,8 @@ func (api *inwxAPI) GetZoneRecords(domain string, meta map[string]string) (model if rtypeAddDot[record.Type] { if record.Type == "MX" && record.Content == "." { // null records don't need to be modified + } else if record.Type == "SRV" && strings.HasSuffix(record.Content, ".") { + // null targets don't need to be modified } else { record.Content = record.Content + "." } diff --git a/providers/transip/retry.go b/providers/transip/retry.go new file mode 100644 index 0000000000..3412ec00d9 --- /dev/null +++ b/providers/transip/retry.go @@ -0,0 +1,76 @@ +package transip + +import ( + "log" + "time" + + "github.com/transip/gotransip/v6/rest" +) + +const ( + initialBackoff = time.Second * 20 // First delay duration + maxBackoff = time.Minute * 4 // Maximum backoff delay +) + +// backoff is the amount of time to sleep if a 429 (or similar) is received. +// It is doubled after each use. +var ( + backoff = initialBackoff +) + +func retryNeeded(err error) bool { + if err == nil { + return false // Not an error. + } + + serr, ok := err.(*rest.Error) + if !ok { + return false // Not an error we know how to work with. + } + + if serr.StatusCode == 200 { + backoff = initialBackoff // Reset + return false // Success! No need to retry. + } + + if serr.StatusCode != 429 { + return false + } + + // a simple exponential back-off + log.Printf("Pausing due to ratelimit (%03d): %v seconds\n", serr.StatusCode, backoff) + time.Sleep(backoff) + backoff = backoff + (backoff / 2) + if backoff > maxBackoff { + backoff = maxBackoff + } + + return true // Request the API call be re-tried. +} + +/* + +TODO(tlim): Use X-Rate-Limit-Reset to optimize sleep time. + +This is a rather lazy implementation. A better implementation would examine +the X-Rate-Limit-Reset header and wait until that timestamp, if the timestamp +seems reasonable. This implementation just does an exponential back-off. + +This is what the documentation says: + +> **Rate limit** +> The rate limit for this API uses a sliding window of 15 minutes. Within this window, a maximum of 1000 requests can be made per user. +> +> Every request returns the following headers, indicating the number of requests made within this window, the amount of requests remaining and the reset timestamp. +> +> ```text +> X-Rate-Limit-Limit: 1000 +> X-Rate-Limit-Remaining: 650 +> X-Rate-Limit-Reset: 1485875578 +> ``` + +> When this rate limit is exceeded, the response contains an error with HTTP status code: `429: Too many requests`. + + + +*/ diff --git a/providers/transip/transipProvider.go b/providers/transip/transipProvider.go index 695a811f43..b471821c9e 100644 --- a/providers/transip/transipProvider.go +++ b/providers/transip/transipProvider.go @@ -99,10 +99,15 @@ func init() { func (n *transipProvider) ListZones() ([]string, error) { var domains []string +retry: domainsMap, err := n.domains.GetAll() + if retryNeeded(err) { + goto retry + } if err != nil { return nil, err } + for _, domainname := range domainsMap { domains = append(domains, domainname.Name) } @@ -136,7 +141,13 @@ func (n *transipProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, cur return err } - return n.domains.ReplaceDNSEntries(dc.Name, nativeDNSEntries) + retry: + err = n.domains.ReplaceDNSEntries(dc.Name, nativeDNSEntries) + if retryNeeded(err) { + goto retry + } + return err + }, }, } @@ -146,7 +157,11 @@ func (n *transipProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, cur // GetZoneRecords returns all records within given zone func (n *transipProvider) GetZoneRecords(domainName string, meta map[string]string) (models.Records, error) { +retry: entries, err := n.domains.GetDNSEntries(domainName) + if retryNeeded(err) { + goto retry + } if err != nil { return nil, err } @@ -167,10 +182,15 @@ func (n *transipProvider) GetZoneRecords(domainName string, meta map[string]stri func (n *transipProvider) GetNameservers(domainName string) ([]*models.Nameserver, error) { var nss []string +retry: entries, err := n.domains.GetNameservers(domainName) + if retryNeeded(err) { + goto retry + } if err != nil { return nil, err } + for _, entry := range entries { nss = append(nss, entry.Hostname) }