mirror of
https://github.com/StackExchange/dnscontrol.git
synced 2025-09-07 05:34:43 +08:00
LUADNS: Support HTTPS record type, enable concurrency, adopt newest SDK (#3640)
This commit is contained in:
parent
6fdbac726b
commit
e9b49e23aa
5 changed files with 176 additions and 300 deletions
|
@ -118,7 +118,7 @@ Jump to a table:
|
|||
| [`INWX`](inwx.md) | ❔ | ✅ | ✅ | ✅ |
|
||||
| [`LINODE`](linode.md) | ❔ | ❌ | ❌ | ✅ |
|
||||
| [`LOOPIA`](loopia.md) | ❔ | ✅ | ❌ | ✅ |
|
||||
| [`LUADNS`](luadns.md) | ❔ | ✅ | ✅ | ✅ |
|
||||
| [`LUADNS`](luadns.md) | ✅ | ✅ | ✅ | ✅ |
|
||||
| [`MYTHICBEASTS`](mythicbeasts.md) | ✅ | ✅ | ❌ | ✅ |
|
||||
| [`NAMECHEAP`](namecheap.md) | ✅ | ❌ | ❌ | ✅ |
|
||||
| [`NAMEDOTCOM`](namedotcom.md) | ❔ | ✅ | ❌ | ✅ |
|
||||
|
@ -278,7 +278,7 @@ Jump to a table:
|
|||
| [`INWX`](inwx.md) | ✅ | ✅ | ✅ | ✅ |
|
||||
| [`LINODE`](linode.md) | ✅ | ❔ | ❔ | ❔ |
|
||||
| [`LOOPIA`](loopia.md) | ✅ | ❔ | ✅ | ✅ |
|
||||
| [`LUADNS`](luadns.md) | ✅ | ❔ | ✅ | ✅ |
|
||||
| [`LUADNS`](luadns.md) | ✅ | ✅ | ✅ | ✅ |
|
||||
| [`MYTHICBEASTS`](mythicbeasts.md) | ✅ | ❔ | ✅ | ✅ |
|
||||
| [`NAMECHEAP`](namecheap.md) | ✅ | ❔ | ❔ | ❌ |
|
||||
| [`NETCUP`](netcup.md) | ✅ | ❔ | ❔ | ❔ |
|
||||
|
|
1
go.mod
1
go.mod
|
@ -70,6 +70,7 @@ require (
|
|||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.157
|
||||
github.com/kylelemons/godebug v1.1.0
|
||||
github.com/luadns/luadns-go v0.3.0
|
||||
github.com/mattn/go-isatty v0.0.20
|
||||
github.com/oracle/oci-go-sdk/v65 v65.95.1
|
||||
github.com/vultr/govultr/v2 v2.17.2
|
||||
|
|
2
go.sum
2
go.sum
|
@ -298,6 +298,8 @@ github.com/lestrrat-go/httpcc v1.0.0/go.mod h1:tGS/u00Vh5N6FHNkExqGGNId8e0Big+++
|
|||
github.com/lestrrat-go/iter v1.0.1/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc=
|
||||
github.com/lestrrat-go/jwx v1.2.7/go.mod h1:bw24IXWbavc0R2RsOtpXL7RtMyP589yZ1+L7kd09ZGA=
|
||||
github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
|
||||
github.com/luadns/luadns-go v0.3.0 h1:mN2yhFv/LnGvPw/HmvYUhXe+lc95oXUqjlYVeJeOJng=
|
||||
github.com/luadns/luadns-go v0.3.0/go.mod h1:DmPXbrGMpynq1YNDpvgww3NP5Zf4wXM5raAbGrp5L+8=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=
|
||||
|
|
|
@ -1,261 +0,0 @@
|
|||
package luadns
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/StackExchange/dnscontrol/v4/models"
|
||||
)
|
||||
|
||||
// Api layer for LuaDNS
|
||||
|
||||
const (
|
||||
apiURL = "https://api.luadns.com/v1"
|
||||
)
|
||||
|
||||
type luadnsProvider struct {
|
||||
domainIndex map[string]uint32
|
||||
nameserversNames []string
|
||||
creds struct {
|
||||
email string
|
||||
apikey string
|
||||
}
|
||||
}
|
||||
|
||||
type errorResponse struct {
|
||||
Status string `json:"status"`
|
||||
RequestID string `json:"request_id"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type userInfoResponse struct {
|
||||
Email string `json:"email"`
|
||||
Name string `json:"name"`
|
||||
TTL uint32 `json:"ttl"`
|
||||
NameServers []string `json:"name_servers"`
|
||||
}
|
||||
|
||||
type zoneRecord struct {
|
||||
ID uint32 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type zoneResponse []zoneRecord
|
||||
|
||||
type domainRecord struct {
|
||||
ID uint32 `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name"`
|
||||
Content string `json:"content"`
|
||||
TTL uint32 `json:"ttl"`
|
||||
}
|
||||
|
||||
type recordResponse []domainRecord
|
||||
|
||||
type (
|
||||
requestParams map[string]string
|
||||
jsonRequestParams map[string]any
|
||||
)
|
||||
|
||||
func (l *luadnsProvider) fetchAvailableNameservers() error {
|
||||
l.nameserversNames = nil
|
||||
bodyString, err := l.get("/users/me", "GET", requestParams{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed fetching available nameservers list from LuaDNS: %w", err)
|
||||
}
|
||||
var ui userInfoResponse
|
||||
if err := json.Unmarshal(bodyString, &ui); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal available nameservers list from LuaDNS: %w", err)
|
||||
}
|
||||
l.nameserversNames = ui.NameServers
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *luadnsProvider) fetchDomainList() error {
|
||||
l.domainIndex = map[string]uint32{}
|
||||
bodyString, err := l.get("/zones", "GET", requestParams{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed fetching domain list from LuaDNS: %w", err)
|
||||
}
|
||||
var dr zoneResponse
|
||||
if err := json.Unmarshal(bodyString, &dr); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal domain list from LuaDNS: %w", err)
|
||||
}
|
||||
for _, domain := range dr {
|
||||
l.domainIndex[domain.Name] = domain.ID
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *luadnsProvider) getDomainID(name string) (uint32, error) {
|
||||
if l.domainIndex == nil {
|
||||
if err := l.fetchDomainList(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
id, ok := l.domainIndex[name]
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("'%s' not a zone in luadns account", name)
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func (l *luadnsProvider) createDomain(domain string) error {
|
||||
params := jsonRequestParams{
|
||||
"name": domain,
|
||||
}
|
||||
if _, err := l.get("/zones", "POST", params); err != nil {
|
||||
return fmt.Errorf("failed create domain (LuaDNS): %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *luadnsProvider) createRecord(domainID uint32, rec jsonRequestParams) error {
|
||||
if _, err := l.get(fmt.Sprintf("/zones/%d/records", domainID), "POST", rec); err != nil {
|
||||
return fmt.Errorf("failed create record (LuaDNS): %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *luadnsProvider) deleteRecord(domainID uint32, recordID uint32) error {
|
||||
if _, err := l.get(fmt.Sprintf("/zones/%d/records/%d", domainID, recordID), "DELETE", requestParams{}); err != nil {
|
||||
return fmt.Errorf("failed delete record (LuaDNS): %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *luadnsProvider) modifyRecord(domainID uint32, recordID uint32, rec jsonRequestParams) error {
|
||||
if _, err := l.get(fmt.Sprintf("/zones/%d/records/%d", domainID, recordID), "PUT", rec); err != nil {
|
||||
return fmt.Errorf("failed update (LuaDNS): %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *luadnsProvider) getRecords(domainID uint32) ([]domainRecord, error) {
|
||||
bodyString, err := l.get(fmt.Sprintf("/zones/%d/records", domainID), "GET", requestParams{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed fetching record list from LuaDNS: %w", err)
|
||||
}
|
||||
var dr recordResponse
|
||||
if err := json.Unmarshal(bodyString, &dr); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal record response from LuaDNS: %w", err)
|
||||
}
|
||||
var records []domainRecord
|
||||
for _, rec := range dr {
|
||||
if rec.Type == "SOA" {
|
||||
continue
|
||||
}
|
||||
records = append(records, rec)
|
||||
}
|
||||
return records, nil
|
||||
}
|
||||
|
||||
func (l *luadnsProvider) get(endpoint string, method string, params any) ([]byte, error) {
|
||||
client := &http.Client{}
|
||||
req, err := l.makeRequest(endpoint, method, params)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
req.Header.Set("Accept", "application/json")
|
||||
req.SetBasicAuth(l.creds.email, l.creds.apikey)
|
||||
// LuaDNS has a rate limit of 1200 request per 5 minute.
|
||||
// So we do a very primitive rate limiting here - delay every request for 250ms - so max. 4 requests/second.
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
bodyString, _ := io.ReadAll(resp.Body)
|
||||
return bodyString, nil
|
||||
}
|
||||
|
||||
bodyString, _ := io.ReadAll(resp.Body)
|
||||
|
||||
var errResp errorResponse
|
||||
err = json.Unmarshal(bodyString, &errResp)
|
||||
if err != nil {
|
||||
return bodyString, fmt.Errorf("LuaDNS API Error: %s URL:%s%s", string(bodyString), req.Host, req.URL.RequestURI())
|
||||
}
|
||||
return bodyString, fmt.Errorf("LuaDNS API error: %s URL:%s%s", errResp.Message, req.Host, req.URL.RequestURI())
|
||||
}
|
||||
|
||||
func (l *luadnsProvider) makeRequest(endpoint string, method string, params any) (*http.Request, error) {
|
||||
switch v := params.(type) {
|
||||
case requestParams:
|
||||
req, err := http.NewRequest(method, apiURL+endpoint, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
q := req.URL.Query()
|
||||
for pName, pValue := range v {
|
||||
q.Add(pName, pValue)
|
||||
}
|
||||
req.URL.RawQuery = q.Encode()
|
||||
return req, nil
|
||||
case jsonRequestParams:
|
||||
requestJSON, err := json.Marshal(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req, err := http.NewRequest(method, apiURL+endpoint, bytes.NewBuffer(requestJSON))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
return req, nil
|
||||
default:
|
||||
return nil, errors.New("invalid request type")
|
||||
}
|
||||
}
|
||||
|
||||
func nativeToRecord(domain string, r *domainRecord) (*models.RecordConfig, error) {
|
||||
rc := &models.RecordConfig{
|
||||
Type: r.Type,
|
||||
TTL: r.TTL,
|
||||
Original: r,
|
||||
}
|
||||
rc.SetLabelFromFQDN(r.Name, domain)
|
||||
var err error
|
||||
switch rtype := rc.Type; rtype {
|
||||
case "TXT":
|
||||
err = rc.SetTargetTXT(r.Content)
|
||||
default:
|
||||
err = rc.PopulateFromString(rtype, r.Content, domain)
|
||||
}
|
||||
return rc, err
|
||||
}
|
||||
|
||||
func recordsToNative(rc *models.RecordConfig) jsonRequestParams {
|
||||
r := jsonRequestParams{
|
||||
"name": rc.GetLabelFQDN() + ".",
|
||||
"type": rc.Type,
|
||||
"ttl": rc.TTL,
|
||||
}
|
||||
switch rtype := rc.Type; rtype {
|
||||
case "TXT":
|
||||
r["content"] = rc.GetTargetTXTJoined()
|
||||
default:
|
||||
r["content"] = rc.GetTargetCombined()
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func checkNS(dc *models.DomainConfig) {
|
||||
newList := make([]*models.RecordConfig, 0, len(dc.Records))
|
||||
for _, rec := range dc.Records {
|
||||
// LuaDNS does not support changing the TTL of the default nameservers, so forcefully change the TTL to 86400.
|
||||
if rec.Type == "NS" && strings.HasSuffix(rec.GetTargetField(), ".luadns.net.") && rec.TTL != 86400 {
|
||||
rec.TTL = 86400
|
||||
}
|
||||
newList = append(newList, rec)
|
||||
}
|
||||
dc.Records = newList
|
||||
}
|
|
@ -1,9 +1,14 @@
|
|||
package luadns
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
api "github.com/luadns/luadns-go"
|
||||
"golang.org/x/time/rate"
|
||||
|
||||
"github.com/StackExchange/dnscontrol/v4/models"
|
||||
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
|
||||
|
@ -23,9 +28,10 @@ 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.Unimplemented(),
|
||||
providers.CanConcur: providers.Can(),
|
||||
providers.CanUseAlias: providers.Can(),
|
||||
providers.CanUseCAA: providers.Can(),
|
||||
providers.CanUseHTTPS: providers.Can(),
|
||||
providers.CanUseLOC: providers.Cannot(),
|
||||
providers.CanUsePTR: providers.Can(),
|
||||
providers.CanUseSRV: providers.Can(),
|
||||
|
@ -47,30 +53,38 @@ func init() {
|
|||
providers.RegisterMaintainer(providerName, providerMaintainer)
|
||||
}
|
||||
|
||||
type luadnsProvider struct {
|
||||
provider *api.Client
|
||||
ctx context.Context
|
||||
rateLimiter *rate.Limiter
|
||||
nameServers []string
|
||||
zones []*api.Zone
|
||||
}
|
||||
|
||||
// NewLuaDNS creates the provider.
|
||||
func NewLuaDNS(m map[string]string, metadata json.RawMessage) (providers.DNSServiceProvider, error) {
|
||||
l := &luadnsProvider{}
|
||||
l.creds.email, l.creds.apikey = m["email"], m["apikey"]
|
||||
if l.creds.email == "" || l.creds.apikey == "" {
|
||||
if m["email"] == "" || m["apikey"] == "" {
|
||||
return nil, errors.New("missing LuaDNS email or apikey")
|
||||
}
|
||||
|
||||
// Get a domain to validate authentication
|
||||
if err := l.fetchDomainList(); err != nil {
|
||||
ctx := context.Background()
|
||||
rateLimiter := rate.NewLimiter(4, 1)
|
||||
provider := api.NewClient(m["email"], m["apikey"])
|
||||
user, err := provider.Me(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
l := &luadnsProvider{
|
||||
provider: provider,
|
||||
ctx: ctx,
|
||||
rateLimiter: rateLimiter,
|
||||
nameServers: user.NameServers,
|
||||
}
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// GetNameservers returns the nameservers for a domain.
|
||||
func (l *luadnsProvider) GetNameservers(domain string) ([]*models.Nameserver, error) {
|
||||
if len(l.nameserversNames) == 0 {
|
||||
if err := l.fetchAvailableNameservers(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return models.ToNameserversStripTD(l.nameserversNames)
|
||||
return models.ToNameserversStripTD(l.nameServers)
|
||||
}
|
||||
|
||||
// ListZones returns a list of the DNS zones.
|
||||
|
@ -78,26 +92,26 @@ func (l *luadnsProvider) ListZones() ([]string, error) {
|
|||
if err := l.fetchDomainList(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
zones := make([]string, 0, len(l.domainIndex))
|
||||
for d := range l.domainIndex {
|
||||
zones = append(zones, d)
|
||||
zoneList := make([]string, 0, len(l.zones))
|
||||
for _, d := range l.zones {
|
||||
zoneList = append(zoneList, d.Name)
|
||||
}
|
||||
return zones, nil
|
||||
return zoneList, nil
|
||||
}
|
||||
|
||||
// GetZoneRecords gets the records of a zone and returns them in RecordConfig format.
|
||||
func (l *luadnsProvider) GetZoneRecords(domain string, meta map[string]string) (models.Records, error) {
|
||||
domainID, err := l.getDomainID(domain)
|
||||
zone, err := l.getZone(domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
records, err := l.getRecords(domainID)
|
||||
records, err := l.getRecords(zone)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
existingRecords := make([]*models.RecordConfig, len(records))
|
||||
for i := range records {
|
||||
newr, err := nativeToRecord(domain, &records[i])
|
||||
newr, err := nativeToRecord(domain, records[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -112,7 +126,7 @@ func (l *luadnsProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, reco
|
|||
|
||||
checkNS(dc)
|
||||
|
||||
domainID, err := l.getDomainID(dc.Name)
|
||||
zone, err := l.getZone(dc.Name)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
@ -130,11 +144,11 @@ func (l *luadnsProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, reco
|
|||
case diff2.REPORT:
|
||||
corrs = []*models.Correction{{Msg: change.MsgsJoined}}
|
||||
case diff2.CREATE:
|
||||
corrs = l.makeCreateCorrection(change.New[0], domainID, msg)
|
||||
corrs = l.makeCreateCorrection(change.New[0], zone, msg)
|
||||
case diff2.CHANGE:
|
||||
corrs = l.makeChangeCorrection(change.Old[0], change.New[0], domainID, msg)
|
||||
corrs = l.makeChangeCorrection(change.Old[0], change.New[0], zone, msg)
|
||||
case diff2.DELETE:
|
||||
corrs = l.makeDeleteCorrection(change.Old[0], domainID, msg)
|
||||
corrs = l.makeDeleteCorrection(change.Old[0], zone, msg)
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled inst.Type %s", change.Type))
|
||||
}
|
||||
|
@ -143,46 +157,166 @@ func (l *luadnsProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, reco
|
|||
return corrections, actualChangeCount, nil
|
||||
}
|
||||
|
||||
func (l *luadnsProvider) makeCreateCorrection(newrec *models.RecordConfig, domainID uint32, msg string) []*models.Correction {
|
||||
func (l *luadnsProvider) makeCreateCorrection(newrec *models.RecordConfig, zone *api.Zone, msg string) []*models.Correction {
|
||||
req := recordsToNative(newrec)
|
||||
return []*models.Correction{{
|
||||
Msg: msg,
|
||||
F: func() error {
|
||||
return l.createRecord(domainID, req)
|
||||
if err := l.rateLimiter.Wait(l.ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := l.provider.CreateRecord(l.ctx, zone, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}}
|
||||
}
|
||||
|
||||
func (l *luadnsProvider) makeChangeCorrection(oldrec *models.RecordConfig, newrec *models.RecordConfig, domainID uint32, msg string) []*models.Correction {
|
||||
recordID := oldrec.Original.(*domainRecord).ID
|
||||
func (l *luadnsProvider) makeChangeCorrection(oldrec *models.RecordConfig, newrec *models.RecordConfig, zone *api.Zone, msg string) []*models.Correction {
|
||||
recordID := oldrec.Original.(*api.Record).ID
|
||||
req := recordsToNative(newrec)
|
||||
return []*models.Correction{{
|
||||
Msg: fmt.Sprintf("%s, LuaDNS ID: %d", msg, recordID),
|
||||
F: func() error {
|
||||
return l.modifyRecord(domainID, recordID, req)
|
||||
if err := l.rateLimiter.Wait(l.ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := l.provider.UpdateRecord(l.ctx, zone, recordID, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}}
|
||||
}
|
||||
|
||||
func (l *luadnsProvider) makeDeleteCorrection(deleterec *models.RecordConfig, domainID uint32, msg string) []*models.Correction {
|
||||
recordID := deleterec.Original.(*domainRecord).ID
|
||||
func (l *luadnsProvider) makeDeleteCorrection(deleterec *models.RecordConfig, zone *api.Zone, msg string) []*models.Correction {
|
||||
recordID := deleterec.Original.(*api.Record).ID
|
||||
return []*models.Correction{{
|
||||
Msg: fmt.Sprintf("%s, LuaDNS ID: %d", msg, recordID),
|
||||
F: func() error {
|
||||
return l.deleteRecord(domainID, recordID)
|
||||
if err := l.rateLimiter.Wait(l.ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := l.provider.DeleteRecord(l.ctx, zone, recordID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}}
|
||||
}
|
||||
|
||||
// EnsureZoneExists creates a zone if it does not exist
|
||||
func (l *luadnsProvider) EnsureZoneExists(domain string) error {
|
||||
if l.domainIndex == nil {
|
||||
if l.zones == nil {
|
||||
if err := l.fetchDomainList(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if _, ok := l.domainIndex[domain]; ok {
|
||||
return nil
|
||||
if err := l.rateLimiter.Wait(l.ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
return l.createDomain(domain)
|
||||
zone, err := l.provider.CreateZone(l.ctx, &api.Zone{Name: domain})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.zones = append(l.zones, zone)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *luadnsProvider) fetchDomainList() error {
|
||||
if err := l.rateLimiter.Wait(l.ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
zones, err := l.provider.ListZones(l.ctx, &api.ListParams{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.zones = zones
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *luadnsProvider) getZone(name string) (*api.Zone, error) {
|
||||
if l.zones == nil {
|
||||
if err := l.fetchDomainList(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for i := range l.zones {
|
||||
if l.zones[i].Name == name {
|
||||
return l.zones[i], nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("'%s' not a zone in luadns account", name)
|
||||
}
|
||||
|
||||
func (l *luadnsProvider) getRecords(zone *api.Zone) ([]*api.Record, error) {
|
||||
if err := l.rateLimiter.Wait(l.ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
records, err := l.provider.ListRecords(l.ctx, zone, &api.ListParams{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var newRecords []*api.Record
|
||||
for _, rec := range records {
|
||||
if rec.Type == "SOA" {
|
||||
continue
|
||||
}
|
||||
newRecords = append(newRecords, rec)
|
||||
}
|
||||
return newRecords, nil
|
||||
}
|
||||
|
||||
func nativeToRecord(domain string, r *api.Record) (*models.RecordConfig, error) {
|
||||
rc := &models.RecordConfig{
|
||||
Type: r.Type,
|
||||
TTL: r.TTL,
|
||||
Original: r,
|
||||
}
|
||||
rc.SetLabelFromFQDN(r.Name, domain)
|
||||
var err error
|
||||
switch rtype := rc.Type; rtype {
|
||||
case "TXT":
|
||||
err = rc.SetTargetTXT(r.Content)
|
||||
default:
|
||||
err = rc.PopulateFromString(rtype, r.Content, domain)
|
||||
}
|
||||
return rc, err
|
||||
}
|
||||
|
||||
func recordsToNative(rc *models.RecordConfig) *api.Record {
|
||||
r := &api.Record{
|
||||
Name: rc.GetLabelFQDN() + ".",
|
||||
Type: rc.Type,
|
||||
TTL: rc.TTL,
|
||||
}
|
||||
switch rtype := rc.Type; rtype {
|
||||
case "TXT":
|
||||
r.Content = rc.GetTargetTXTJoined()
|
||||
case "HTTPS":
|
||||
content := fmt.Sprintf("%d %s %s", rc.SvcPriority, rc.GetTargetField(), rc.SvcParams)
|
||||
if rc.SvcParams == "" {
|
||||
content = content[:len(content)-1]
|
||||
}
|
||||
r.Content = content
|
||||
default:
|
||||
r.Content = rc.GetTargetCombined()
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func checkNS(dc *models.DomainConfig) {
|
||||
newList := make([]*models.RecordConfig, 0, len(dc.Records))
|
||||
for _, rec := range dc.Records {
|
||||
// LuaDNS does not support changing the TTL of the default nameservers, so forcefully change the TTL to 86400.
|
||||
if rec.Type == "NS" && strings.HasSuffix(rec.GetTargetField(), ".luadns.net.") && rec.TTL != 86400 {
|
||||
rec.TTL = 86400
|
||||
}
|
||||
newList = append(newList, rec)
|
||||
}
|
||||
dc.Records = newList
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue