AUTODNS: enable concurrent gathering of zones (#3494)

Co-authored-by: Tom Limoncelli <tlimoncelli@stackoverflow.com>
This commit is contained in:
arnoschoon 2025-03-21 15:58:19 +01:00 committed by GitHub
parent c5b3ccc7db
commit fef79fb84d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 53 additions and 13 deletions

View file

@ -5,8 +5,10 @@ import (
"encoding/json"
"errors"
"io"
"math"
"net/http"
"sort"
"time"
"github.com/StackExchange/dnscontrol/v4/models"
)
@ -25,7 +27,15 @@ type ZoneListRequest struct {
Filter []*ZoneListFilter `json:"filters"`
}
var (
// Default retry configuration
defaultRetryWait = 3 * time.Second
defaultRetryMax = 4
)
func (api *autoDNSProvider) request(method string, requestPath string, data interface{}) ([]byte, error) {
var retryCounter = 0
client := &http.Client{}
requestURL := api.baseURL
@ -43,18 +53,35 @@ func (api *autoDNSProvider) request(method string, requestPath string, data inte
request.Body = io.NopCloser(buffer)
}
response, err := client.Do(request)
if err != nil {
return nil, err
}
defer response.Body.Close()
for {
response, err := client.Do(request)
if err != nil {
return nil, err
}
defer response.Body.Close()
responseText, _ := io.ReadAll(response.Body)
if response.StatusCode != http.StatusOK {
return nil, errors.New("Request to " + requestURL.Path + " failed: " + string(responseText))
responseText, _ := io.ReadAll(response.Body)
if response.StatusCode != http.StatusOK && response.StatusCode != http.StatusTooManyRequests {
return nil, errors.New("Request to " + requestURL.Path + " failed: " + string(responseText))
}
if response.StatusCode == http.StatusOK {
return responseText, nil
}
if retryCounter == defaultRetryMax { // the condition stops matching
break // break out of the loop
}
retryCounter++
sleepDuration := time.Duration(math.Pow(2, float64(retryCounter)) * float64(defaultRetryWait))
time.Sleep(sleepDuration)
}
return responseText, nil
return nil, errors.New("Failed to fetch" + requestURL.Path + " after 4 retries")
}
func (api *autoDNSProvider) findZoneSystemNameServer(domain string) (*models.Nameserver, error) {
@ -89,7 +116,12 @@ func (api *autoDNSProvider) getZone(domain string) (*Zone, error) {
}
// if resolving of a systemNameServer succeeds the system contains this zone
responseData, _ := api.request("GET", "zone/"+domain+"/"+systemNameServer.Name, nil)
responseData, err2 := api.request("GET", "zone/"+domain+"/"+systemNameServer.Name, nil)
if err2 != nil {
return nil, err2
}
var responseObject JSONResponseDataZone
// make sure that the response is valid, the zone is in AutoDNS but we're not sure the returned data meets our expectation
unmErr := json.Unmarshal(responseData, &responseObject)
@ -106,7 +138,11 @@ func (api *autoDNSProvider) updateZone(domain string, resourceRecords []*Resourc
return err
}
zone, _ := api.getZone(domain)
zone, err2 := api.getZone(domain)
if err2 != nil {
return err2
}
zone.Origin = domain
zone.SystemNameServer = systemNameServer.Name

View file

@ -19,7 +19,7 @@ var features = providers.DocumentationNotes{
// The default for unlisted capabilities is 'Cannot'.
// See providers/capabilities.go for the entire list of capabilities.
providers.CanGetZones: providers.Can(),
providers.CanConcur: providers.Cannot(),
providers.CanConcur: providers.Can(),
providers.CanUseAlias: providers.Can(),
providers.CanUseCAA: providers.Can(),
providers.CanUseDS: providers.Cannot(),
@ -174,7 +174,11 @@ func (api *autoDNSProvider) GetNameservers(domain string) ([]*models.Nameserver,
// GetZoneRecords gets the records of a zone and returns them in RecordConfig format.
func (api *autoDNSProvider) GetZoneRecords(domain string, meta map[string]string) (models.Records, error) {
zone, _ := api.getZone(domain)
zone, err := api.getZone(domain)
if err != nil {
return nil, err
}
existingRecords := make([]*models.RecordConfig, len(zone.ResourceRecords))
for i, resourceRecord := range zone.ResourceRecords {
var err error