PORKBUN: Improve retry handling, mark as concurrent (#3652)

This commit is contained in:
James O'Gorman 2025-07-09 17:03:59 +01:00 committed by GitHub
parent cd8892f9bb
commit 4ce19352e9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 38 additions and 30 deletions

View file

@ -16,12 +16,10 @@ Our general philosophy is:
- Anywhere we have a special case for a particular Rtype, we use a `switch` statement and have a `case` for every single record type, usually with a `default:` case that calls `panic()`. This way developers adding a new record type will quickly find where they need to add code (the panic will tell them where). Before we did this, missing implementation code would go unnoticed for months.
- Keep things alphabetical. If you are adding your record type to a case statement, function library, or whatever, please list it alphabetically along with the others when possible.
Step 2 requires `stringer`.
Step 2 requires `stringer` which is available using
```shell
go install golang.org/x/tools/cmd/stringer@latest
go tool stringer
```
You may need to symlink stringer into your PATH.
## Step 1: Update `RecordConfig` in `models/record.go`
If the record has any unique fields, add them to `RecordConfig`.
@ -61,7 +59,7 @@ a minimum.
```shell
pushd; cd providers/;
stringer -type=Capability
go tool stringer -type=Capability
popd
```
alternatively

View file

@ -129,7 +129,7 @@ Jump to a table:
| [`ORACLE`](oracle.md) | ❔ | ✅ | ✅ | ✅ |
| [`OVH`](ovh.md) | ❔ | ✅ | ❌ | ✅ |
| [`PACKETFRAME`](packetframe.md) | ❔ | ❌ | ❌ | ❔ |
| [`PORKBUN`](porkbun.md) | | ❌ | ❌ | ✅ |
| [`PORKBUN`](porkbun.md) | | ❌ | ❌ | ✅ |
| [`POWERDNS`](powerdns.md) | ❔ | ✅ | ✅ | ✅ |
| [`REALTIMEREGISTER`](realtimeregister.md) | ❔ | ❌ | ✅ | ✅ |
| [`ROUTE53`](route53.md) | ✅ | ✅ | ✅ | ✅ |

3
go.mod
View file

@ -62,6 +62,7 @@ require (
github.com/G-Core/gcore-dns-sdk-go v0.3.1
github.com/centralnicgroup-opensource/rtldev-middleware-go-sdk/v5 v5.0.15
github.com/containrrr/shoutrrr v0.8.0
github.com/failsafe-go/failsafe-go v0.6.9
github.com/fatih/color v1.18.0
github.com/fbiville/markdown-table-formatter v0.3.0
github.com/go-acme/lego/v4 v4.24.0
@ -167,3 +168,5 @@ require (
gopkg.in/sourcemap.v1 v1.0.5 // indirect
moul.io/http2curl v1.0.0 // indirect
)
tool golang.org/x/tools/cmd/stringer

4
go.sum
View file

@ -81,6 +81,8 @@ github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6/go.mod h1
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/billputer/go-namecheap v0.0.0-20210108011502-994a912fb7f9 h1:2vQTbEJvFsyd1VefzZ34GUkUD6TkJleYYJh9/25WBE4=
github.com/billputer/go-namecheap v0.0.0-20210108011502-994a912fb7f9/go.mod h1:bqqNsI2akL+lLWyApkYY0cxquWPKwEBU0Wd3chi3TEg=
github.com/bits-and-blooms/bitset v1.14.3 h1:Gd2c8lSNf9pKXom5JtD7AaKO8o7fGQ2LtFj1436qilA=
github.com/bits-and-blooms/bitset v1.14.3/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs=
github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
@ -126,6 +128,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/exoscale/egoscale v0.102.4 h1:GBKsZMIOzwBfSu+4ZmWka3Ejf2JLiaBDHp4CQUgvp2E=
github.com/exoscale/egoscale v0.102.4/go.mod h1:ROSmPtle0wvf91iLZb09++N/9BH2Jo9XxIpAEumvocA=
github.com/failsafe-go/failsafe-go v0.6.9 h1:7HWEzOlFOjNerxgWd8onWA2j/aEuqyAtuX6uWya/364=
github.com/failsafe-go/failsafe-go v0.6.9/go.mod h1:zb7xfp1/DJ7Mn4xJhVSZ9F2qmmMEGvYHxEOHYK5SIm0=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=

View file

@ -1,6 +1,6 @@
package diff2
//go:generate stringer -type=Verb
//go:generate go tool stringer -type=Verb
// This module provides functions that "diff" the existing records
// against the desired records.

View file

@ -1,4 +1,4 @@
//go:generate stringer -type=State
//go:generate go tool stringer -type=State
package txtutil

View file

@ -1,4 +1,4 @@
//go:generate stringer -type=State
//go:generate go tool stringer -type=State
package txtutil

View file

@ -1,4 +1,4 @@
//go:generate stringer -type=Capability
//go:generate go tool stringer -type=Capability
package providers

View file

@ -12,6 +12,9 @@ import (
"time"
"github.com/StackExchange/dnscontrol/v4/pkg/printer"
"github.com/failsafe-go/failsafe-go"
"github.com/failsafe-go/failsafe-go/failsafehttp"
"github.com/failsafe-go/failsafe-go/retrypolicy"
)
const (
@ -65,37 +68,37 @@ func (c *porkbunProvider) post(endpoint string, params requestParams) ([]byte, e
params["apikey"] = c.apiKey
params["secretapikey"] = c.secretKey
personJSON, err := json.Marshal(params)
paramsJSON, err := json.Marshal(params)
if err != nil {
return []byte{}, err
}
client := &http.Client{}
req, _ := http.NewRequest(http.MethodPost, baseURL+endpoint, bytes.NewBuffer(personJSON))
retryPolicy := failsafehttp.RetryPolicyBuilder().
WithMaxRetries(5).
// Exponential backoff between 1 and 10 seconds
WithBackoff(time.Second, 10*time.Second).
WithJitter(100 * time.Millisecond).
OnRetryScheduled(func(f failsafe.ExecutionScheduledEvent[*http.Response]) {
printer.Debugf("Porkbun API response code %d, waiting for %s until next attempt\n", f.LastResult().StatusCode, f.Delay)
}).
Build()
retrycnt := 0
client := &http.Client{
Transport: failsafehttp.NewRoundTripper(nil, retryPolicy),
}
req, _ := http.NewRequest(http.MethodPost, baseURL+endpoint, bytes.NewBuffer(paramsJSON))
// If request sending too fast, the server will fail with the following error:
// porkbun API error: Create error: We were unable to create the DNS record.
retry:
time.Sleep(time.Second)
resp, err := client.Do(req)
if err != nil {
return []byte{}, err
if errors.Is(err, retrypolicy.ErrExceeded) {
// Return the underlying error rather than the wrapped error, which has too much detail
return nil, retrypolicy.ErrExceeded
}
return nil, err
}
bodyString, _ := io.ReadAll(resp.Body)
if resp.StatusCode == http.StatusAccepted || resp.StatusCode == http.StatusServiceUnavailable {
retrycnt++
if retrycnt == 5 {
return bodyString, errors.New("rate limiting exceeded")
}
printer.Warnf("Rate limiting.. waiting for %d second(s)\n", retrycnt*10)
time.Sleep(time.Second * time.Duration(retrycnt*10))
goto retry
}
// Got error from API ?
var errResp errorResponse
err = json.Unmarshal(bodyString, &errResp)

View file

@ -59,7 +59,7 @@ var features = providers.DocumentationNotes{
// See providers/capabilities.go for the entire list of capabilities.
providers.CanAutoDNSSEC: providers.Cannot(),
providers.CanGetZones: providers.Can(),
providers.CanConcur: providers.Unimplemented(),
providers.CanConcur: providers.Can(),
providers.CanUseAlias: providers.Can(),
providers.CanUseCAA: providers.Can(),
providers.CanUseDS: providers.Cannot(),