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" "encoding/json"
"errors" "errors"
"io" "io"
"math"
"net/http" "net/http"
"sort" "sort"
"time"
"github.com/StackExchange/dnscontrol/v4/models" "github.com/StackExchange/dnscontrol/v4/models"
) )
@ -25,7 +27,15 @@ type ZoneListRequest struct {
Filter []*ZoneListFilter `json:"filters"` 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) { func (api *autoDNSProvider) request(method string, requestPath string, data interface{}) ([]byte, error) {
var retryCounter = 0
client := &http.Client{} client := &http.Client{}
requestURL := api.baseURL requestURL := api.baseURL
@ -43,18 +53,35 @@ func (api *autoDNSProvider) request(method string, requestPath string, data inte
request.Body = io.NopCloser(buffer) request.Body = io.NopCloser(buffer)
} }
response, err := client.Do(request) for {
if err != nil { response, err := client.Do(request)
return nil, err if err != nil {
} return nil, err
defer response.Body.Close() }
defer response.Body.Close()
responseText, _ := io.ReadAll(response.Body) responseText, _ := io.ReadAll(response.Body)
if response.StatusCode != http.StatusOK {
return nil, errors.New("Request to " + requestURL.Path + " failed: " + string(responseText)) 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) { 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 // 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 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 // 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) unmErr := json.Unmarshal(responseData, &responseObject)
@ -106,7 +138,11 @@ func (api *autoDNSProvider) updateZone(domain string, resourceRecords []*Resourc
return err return err
} }
zone, _ := api.getZone(domain) zone, err2 := api.getZone(domain)
if err2 != nil {
return err2
}
zone.Origin = domain zone.Origin = domain
zone.SystemNameServer = systemNameServer.Name zone.SystemNameServer = systemNameServer.Name

View file

@ -19,7 +19,7 @@ var features = providers.DocumentationNotes{
// The default for unlisted capabilities is 'Cannot'. // The default for unlisted capabilities is 'Cannot'.
// See providers/capabilities.go for the entire list of capabilities. // See providers/capabilities.go for the entire list of capabilities.
providers.CanGetZones: providers.Can(), providers.CanGetZones: providers.Can(),
providers.CanConcur: providers.Cannot(), providers.CanConcur: providers.Can(),
providers.CanUseAlias: providers.Can(), providers.CanUseAlias: providers.Can(),
providers.CanUseCAA: providers.Can(), providers.CanUseCAA: providers.Can(),
providers.CanUseDS: providers.Cannot(), 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. // 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) { 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)) existingRecords := make([]*models.RecordConfig, len(zone.ResourceRecords))
for i, resourceRecord := range zone.ResourceRecords { for i, resourceRecord := range zone.ResourceRecords {
var err error var err error