diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 4ed646f57..f2c85cc16 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -31,8 +31,6 @@ jobs:
fail-fast: false
matrix:
provider: [AZURE_DNS, BIND, CLOUDFLAREAPI, DIGITALOCEAN, GANDI_V5, GCLOUD, HEXONET, NAMEDOTCOM, ROUTE53]
- exclude:
- - provider: DIGITALOCEAN # remove when #636 is fixed
steps:
- name: Checkout repo
uses: actions/checkout@v2
diff --git a/docs/_includes/matrix.html b/docs/_includes/matrix.html
index bea0e6491..0dbc07c41 100644
--- a/docs/_includes/matrix.html
+++ b/docs/_includes/matrix.html
@@ -472,8 +472,8 @@
|
-
-
+ |
+
|
@@ -888,7 +888,9 @@
| |
|
|
- |
+
+
+ |
|
diff --git a/docs/_providers/digitalocean.md b/docs/_providers/digitalocean.md
index f7c4cb769..bc91f0624 100644
--- a/docs/_providers/digitalocean.md
+++ b/docs/_providers/digitalocean.md
@@ -38,5 +38,8 @@ D("example.tld", REG_NONE, DnsProvider(DIGITALOCEAN),
## Limitations
-- Digialocean DNS doesn't support `;` value with CAA-records ([DigitalOcean documentation](https://www.digitalocean.com/docs/networking/dns/how-to/create-caa-records/))
-- No support for TXT records with multiple strings, as the API prevents espacing quotes.
+- Digitalocean DNS doesn't support `;` value with CAA-records ([DigitalOcean documentation](https://www.digitalocean.com/docs/networking/dns/how-to/create-caa-records/))
+- While Digitalocean DNS supports TXT records with multiple strings,
+ their implementation is lacking. It does not support strings that
+ include double-quotes nor many long strings. The length limits may
+ restrict your ability to use very long DKIM or SPF records.
diff --git a/go.mod b/go.mod
index f6f0cf321..4d461e4ee 100644
--- a/go.mod
+++ b/go.mod
@@ -18,7 +18,7 @@ require (
github.com/billputer/go-namecheap v0.0.0-20170915210158-0c7adb0710f8
github.com/cenkalti/backoff v2.1.1+incompatible // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
- github.com/digitalocean/godo v1.46.0
+ github.com/digitalocean/godo v1.52.0
github.com/dnsimple/dnsimple-go v0.62.0
github.com/exoscale/egoscale v0.23.0
github.com/go-acme/lego v2.7.2+incompatible
@@ -54,12 +54,13 @@ require (
github.com/urfave/cli/v2 v2.2.0
github.com/vultr/govultr v1.0.0
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 // indirect
- golang.org/x/net v0.0.0-20201016165138-7b1cca2348c0
- golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43
+ golang.org/x/net v0.0.0-20201110031124-69a78807bb2b
+ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58
golang.org/x/sync v0.0.0-20201008141435-b3e1573b7520 // indirect
golang.org/x/sys v0.0.0-20201018121011-98379d014ca7 // indirect
golang.org/x/tools v0.0.0-20201017001424-6003fad69a88 // indirect
google.golang.org/api v0.33.0
+ google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20201015140912-32ed001d685c // indirect
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
gopkg.in/ini.v1 v1.42.0 // indirect
diff --git a/go.sum b/go.sum
index bb9228655..156434711 100644
--- a/go.sum
+++ b/go.sum
@@ -105,8 +105,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/digitalocean/godo v1.46.0 h1:WRbwjATilgz2NE4NGMeSDpeicy9h4xSKNGuRJ/Nq/fA=
-github.com/digitalocean/godo v1.46.0/go.mod h1:p7dOjjtSBqCTUksqtA5Fd3uaKs9kyTq2xcz76ulEJRU=
+github.com/digitalocean/godo v1.52.0 h1:1QSUC0w5T1wS1d/1uvPtG8GLeD0p/4zhx1Q+Fxtna+k=
+github.com/digitalocean/godo v1.52.0/go.mod h1:p7dOjjtSBqCTUksqtA5Fd3uaKs9kyTq2xcz76ulEJRU=
github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TRo4=
github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
github.com/dnsimple/dnsimple-go v0.62.0 h1:Lr4U8F08FBL90rMEtQMR547RebifgwTPGCRC/+6ZW4g=
@@ -446,8 +446,8 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R
golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20201016165138-7b1cca2348c0 h1:5kGOVHlq0euqwzgTC9Vu15p6fV1Wi0ArVi8da2urnVg=
-golang.org/x/net v0.0.0-20201016165138-7b1cca2348c0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=
+golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
@@ -457,6 +457,8 @@ golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BG
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43 h1:ld7aEMNHoBnnDAX15v1T6z31v8HwR2A9FYOuAhWqkwc=
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58 h1:Mj83v+wSRNEar42a/MQgxk9X42TdEmrOl9i+y8WbxLo=
+golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -606,6 +608,8 @@ google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpC
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
+google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
diff --git a/integrationTest/integration_test.go b/integrationTest/integration_test.go
index 683999645..968d87a2d 100644
--- a/integrationTest/integration_test.go
+++ b/integrationTest/integration_test.go
@@ -680,9 +680,11 @@ func makeTests(t *testing.T) []*TestGroup {
tc("Change a TXT", txt("foo", "changed")),
clear(),
tc("Create a TXT with spaces", txt("foo", "with spaces")),
- tc("Create 1 TXT as array", txtmulti("foo", []string{"simple"})),
+ tc("Create 1 TXT as array", txtmulti("foo", []string{"simple"})), // Same as non-TXTMulti
clear(),
tc("Create a 255-byte TXT", txt("foo", strings.Repeat("A", 255))),
+ clear(),
+ tc("Create TXT with single-quote", txt("foo", "blah`blah")),
),
testgroup("ws TXT",
@@ -862,6 +864,42 @@ func makeTests(t *testing.T) []*TestGroup {
txtmulti("foo2", []string{"one", "two"}),
txtmulti("foo3", []string{"eh", "bee", "cee"}),
),
+ tc("Change TXTMulti",
+ txtmulti("foo1", []string{"dimple"}),
+ txtmulti("foo2", []string{"fun", "two"}),
+ txtmulti("foo3", []string{"eh", "bzz", "cee"}),
+ ),
+ tc("Long TXTMulti",
+ // This isn't the longest a TXT record can be, but it is the
+ // longest DIGITALOCEAN permits.
+ txtmulti("foo4", []string{strings.Repeat("X", 255), strings.Repeat("Y", 252)}),
+ ),
+ ),
+
+ testgroup("TXTMulti tests that break DO",
+ // DO's implementation of TXTMulti is broken, thus we separate out
+ // tests that fail for it. Users are warned about these limits
+ // in docs/_providers/digitalocean.md
+ requires(providers.CanUseTXTMulti),
+ not("DIGITALOCEAN"),
+ // Digital Ocean's TXT record implementation checks size limits wrong.
+ // RFC 1035 Section 3.3.14 states that each substring can be 255
+ // octets, and there is no limit on the number of such
+ // substrings, aside from the usual packet length limits. DO's
+ // implementation restricts the total length to be 512 octets,
+ // including any backlashes used for escapes, quotes, and other
+ // metachars. In other words, they're doing the checking on the
+ // API protocol encoded data instead of on on the resulting TXT
+ // record.
+ // Proper TXT implementations can handle TXT records like this:
+ tc("3x255-byte TXTMulti",
+ txtmulti("foo3", []string{strings.Repeat("X", 255), strings.Repeat("Y", 255), strings.Repeat("Z", 255)})),
+ clear(),
+ // Digital Ocean's TXT record implementation handles quotes wrong.
+ // It craps out if your TXT record includes double-quotes.
+ // Someone doesn't understand how zonefile-style quoting is
+ // supposed to work.
+ // Proper TXT implementations can handle TXT records like these:
tc("Create TXTMulti with quotes",
txtmulti("foo1", []string{"simple"}),
txtmulti("foo2", []string{"o\"ne", "tw\"o"}),
@@ -869,11 +907,9 @@ func makeTests(t *testing.T) []*TestGroup {
),
tc("Change TXTMulti",
txtmulti("foo1", []string{"dimple"}),
- txtmulti("foo2", []string{"fun", "two"}),
+ txtmulti("foo2", []string{"fun", "t\"wo"}),
txtmulti("foo3", []string{"eh", "bzz", "cee"}),
),
- tc("3x255-byte TXTMulti",
- txtmulti("foo3", []string{strings.Repeat("X", 255), strings.Repeat("Y", 255), strings.Repeat("Z", 255)})),
),
testgroup("DS",
diff --git a/providers/digitalocean/digitaloceanProvider.go b/providers/digitalocean/digitaloceanProvider.go
index 94f795d04..cfd1b7540 100644
--- a/providers/digitalocean/digitaloceanProvider.go
+++ b/providers/digitalocean/digitaloceanProvider.go
@@ -74,11 +74,9 @@ var features = providers.DocumentationNotes{
providers.DocCreateDomains: providers.Can(),
providers.DocOfficiallySupported: providers.Cannot(),
providers.CanUseSRV: providers.Can(),
- // Digitalocean support CAA records, except
- // ";" value with issue/issuewild records:
- // https://www.digitalocean.com/docs/networking/dns/how-to/create-caa-records/
- providers.CanUseCAA: providers.Can(),
- providers.CanGetZones: providers.Can(),
+ providers.CanUseCAA: providers.Can("Semicolons not supported in issue/issuewild fields.", "https://www.digitalocean.com/docs/networking/dns/how-to/create-caa-records"),
+ providers.CanGetZones: providers.Can(),
+ providers.CanUseTXTMulti: providers.Can("A broken parser prevents TXTMulti strings from including double-quotes; The total length of all strings can't be longer than 512; and in reality must be shorter due to sloppy validation checks.", "https://github.com/StackExchange/dnscontrol/issues/370"),
}
func init() {
@@ -329,7 +327,7 @@ func pauseAndRetry(resp *godo.Response) bool {
}
// a simple exponential back-off with a 3-minute max.
- log.Printf("Pausing due to ratelimit: %v seconds\n", backoff)
+ log.Printf("Delaying %v due to ratelimit\n", backoff)
time.Sleep(backoff)
backoff = backoff + (backoff / 2)
if backoff > maxBackoff {