1
0
mirror of https://github.com/StackExchange/dnscontrol.git synced 2024-05-11 05:55:12 +00:00

VULTR: use API v2 (#1768)

This commit is contained in:
Nicolas Lorin
2022-10-13 00:17:15 +02:00
committed by GitHub
parent ee2c7adb05
commit 2898fbad72
4 changed files with 80 additions and 56 deletions

2
go.mod
View File

@@ -51,7 +51,6 @@ require (
github.com/tdewolff/minify/v2 v2.12.3 github.com/tdewolff/minify/v2 v2.12.3
github.com/transip/gotransip/v6 v6.17.0 github.com/transip/gotransip/v6 v6.17.0
github.com/urfave/cli/v2 v2.17.1 github.com/urfave/cli/v2 v2.17.1
github.com/vultr/govultr v1.1.1
github.com/xddxdd/ottoext v0.0.0-20210101073831-439879ee6281 github.com/xddxdd/ottoext v0.0.0-20210101073831-439879ee6281
golang.org/x/net v0.0.0-20221002022538-bcab6841153b golang.org/x/net v0.0.0-20221002022538-bcab6841153b
golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1 golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1
@@ -149,6 +148,7 @@ require (
github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect
github.com/stretchr/objx v0.4.0 // indirect github.com/stretchr/objx v0.4.0 // indirect
github.com/tdewolff/parse/v2 v2.6.4 // indirect github.com/tdewolff/parse/v2 v2.6.4 // indirect
github.com/vultr/govultr/v2 v2.17.2 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
go.opencensus.io v0.23.0 // indirect go.opencensus.io v0.23.0 // indirect
go.uber.org/atomic v1.9.0 // indirect go.uber.org/atomic v1.9.0 // indirect

4
go.sum
View File

@@ -644,8 +644,8 @@ github.com/urfave/cli/v2 v2.17.1/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xM
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/vultr/govultr v1.1.1 h1:ntltxMYyJstjKz1v2CLNxFZiqjlt/8HqD1GZeJ/fAMc= github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs=
github.com/vultr/govultr v1.1.1/go.mod h1:QXCNTRg0nwu95ayiMC3feYvrAFTLnj94s2FiibIpoC4= github.com/vultr/govultr/v2 v2.17.2/go.mod h1:ZFOKGWmgjytfyjeyAdhQlSWwTjh2ig+X49cAp50dzXI=
github.com/xddxdd/ottoext v0.0.0-20210101073831-439879ee6281 h1:QruJ/b9zaFRxtNYvekIXlqWh/BoJJrSJg+FdmMNp8cw= github.com/xddxdd/ottoext v0.0.0-20210101073831-439879ee6281 h1:QruJ/b9zaFRxtNYvekIXlqWh/BoJJrSJg+FdmMNp8cw=
github.com/xddxdd/ottoext v0.0.0-20210101073831-439879ee6281/go.mod h1:8Hr+gV9GtucFQOptKfzuYxqg++CyIjg4E7QCR7F3Dxw= github.com/xddxdd/ottoext v0.0.0-20210101073831-439879ee6281/go.mod h1:8Hr+gV9GtucFQOptKfzuYxqg++CyIjg4E7QCR7F3Dxw=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=

View File

@@ -4,7 +4,7 @@ import (
"testing" "testing"
"github.com/StackExchange/dnscontrol/v3/models" "github.com/StackExchange/dnscontrol/v3/models"
"github.com/vultr/govultr" "github.com/vultr/govultr/v2"
) )
func TestConversion(t *testing.T) { func TestConversion(t *testing.T) {
@@ -12,7 +12,7 @@ func TestConversion(t *testing.T) {
Name: "example.com", Name: "example.com",
} }
records := []*govultr.DNSRecord{ records := []*govultr.DomainRecord{
{ {
Type: "A", Type: "A",
Name: "", Name: "",
@@ -29,14 +29,14 @@ func TestConversion(t *testing.T) {
Type: "SRV", Type: "SRV",
Name: "_ssh_.tcp", Name: "_ssh_.tcp",
Data: "5 22 ssh.example.com", Data: "5 22 ssh.example.com",
Priority: intPtr(5), Priority: 5,
TTL: 300, TTL: 300,
}, },
{ {
Type: "MX", Type: "MX",
Name: "", Name: "",
Data: "mail.example.com", Data: "mail.example.com",
Priority: intPtr(0), Priority: 0,
TTL: 300, TTL: 300,
}, },
{ {
@@ -65,9 +65,9 @@ func TestConversion(t *testing.T) {
t.Error("Error converting Vultr record", record) t.Error("Error converting Vultr record", record)
} }
converted := toVultrRecord(dc, rc, 0) converted := toVultrRecord(dc, rc, "0")
if converted.Type != record.Type || converted.Name != record.Name || converted.Data != record.Data || ((converted.Priority == nil) != (record.Priority == nil) || (converted.Priority != nil && *converted.Priority != *record.Priority)) || converted.TTL != record.TTL { if converted.Type != record.Type || converted.Name != record.Name || converted.Data != record.Data || (converted.Priority != record.Priority) || converted.TTL != record.TTL {
t.Error("Vultr record conversion mismatch", record, rc, converted) t.Error("Vultr record conversion mismatch", record, rc, converted)
} }
} }

View File

@@ -5,13 +5,14 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"strconv"
"strings" "strings"
"golang.org/x/oauth2"
"github.com/StackExchange/dnscontrol/v3/models" "github.com/StackExchange/dnscontrol/v3/models"
"github.com/StackExchange/dnscontrol/v3/pkg/diff" "github.com/StackExchange/dnscontrol/v3/pkg/diff"
"github.com/StackExchange/dnscontrol/v3/providers" "github.com/StackExchange/dnscontrol/v3/providers"
"github.com/vultr/govultr" "github.com/vultr/govultr/v2"
) )
/* /*
@@ -62,27 +63,44 @@ func NewProvider(m map[string]string, metadata json.RawMessage) (providers.DNSSe
return nil, fmt.Errorf("missing Vultr API token") return nil, fmt.Errorf("missing Vultr API token")
} }
client := govultr.NewClient(nil, token) config := &oauth2.Config{}
client := govultr.NewClient(config.Client(context.Background(), &oauth2.Token{AccessToken: token}))
client.SetUserAgent("dnscontrol") client.SetUserAgent("dnscontrol")
_, err := client.Account.GetInfo(context.Background()) _, err := client.Account.Get(context.Background())
return &vultrProvider{client, token}, err return &vultrProvider{client, token}, err
} }
// 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 *vultrProvider) GetZoneRecords(domain string) (models.Records, error) { func (api *vultrProvider) GetZoneRecords(domain string) (models.Records, error) {
records, err := api.client.DNSRecord.List(context.Background(), domain) listOptions := &govultr.ListOptions{}
if err != nil { records, meta, err := api.client.DomainRecord.List(context.Background(), domain, listOptions)
return nil, err curRecords := make(models.Records, meta.Total)
} nextI := 0
curRecords := make(models.Records, len(records)) for {
for i := range records {
r, err := toRecordConfig(domain, &records[i])
if err != nil { if err != nil {
return nil, err return nil, err
} }
curRecords[i] = r currentI := 0
for i, record := range records {
r, err := toRecordConfig(domain, &record)
if err != nil {
return nil, err
}
curRecords[nextI+i] = r
currentI = nextI + i
}
nextI = currentI + 1
if meta.Links.Next == "" {
break
} else {
listOptions.Cursor = meta.Links.Next
records, meta, err = api.client.DomainRecord.List(context.Background(), domain, listOptions)
continue
}
} }
return curRecords, nil return curRecords, nil
@@ -108,31 +126,32 @@ func (api *vultrProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*mode
var corrections []*models.Correction var corrections []*models.Correction
for _, mod := range delete { for _, mod := range delete {
id := mod.Existing.Original.(*govultr.DNSRecord).RecordID id := mod.Existing.Original.(*govultr.DomainRecord).ID
corrections = append(corrections, &models.Correction{ corrections = append(corrections, &models.Correction{
Msg: fmt.Sprintf("%s; Vultr RecordID: %v", mod.String(), id), Msg: fmt.Sprintf("%s; Vultr RecordID: %v", mod.String(), id),
F: func() error { F: func() error {
return api.client.DNSRecord.Delete(context.Background(), dc.Name, strconv.Itoa(id)) return api.client.DomainRecord.Delete(context.Background(), dc.Name, id)
}, },
}) })
} }
for _, mod := range create { for _, mod := range create {
r := toVultrRecord(dc, mod.Desired, 0) r := toVultrRecord(dc, mod.Desired, "0")
corrections = append(corrections, &models.Correction{ corrections = append(corrections, &models.Correction{
Msg: mod.String(), Msg: mod.String(),
F: func() error { F: func() error {
return api.client.DNSRecord.Create(context.Background(), dc.Name, r.Type, r.Name, r.Data, r.TTL, vultrPriority(r)) _, err := api.client.DomainRecord.Create(context.Background(), dc.Name, &govultr.DomainRecordReq{r.Name, r.Type, r.Data, r.TTL, &r.Priority})
return err
}, },
}) })
} }
for _, mod := range modify { for _, mod := range modify {
r := toVultrRecord(dc, mod.Desired, mod.Existing.Original.(*govultr.DNSRecord).RecordID) r := toVultrRecord(dc, mod.Desired, mod.Existing.Original.(*govultr.DomainRecord).ID)
corrections = append(corrections, &models.Correction{ corrections = append(corrections, &models.Correction{
Msg: fmt.Sprintf("%s; Vultr RecordID: %v", mod.String(), r.RecordID), Msg: fmt.Sprintf("%s; Vultr RecordID: %v", mod.String(), r.ID),
F: func() error { F: func() error {
return api.client.DNSRecord.Update(context.Background(), dc.Name, r) return api.client.DomainRecord.Update(context.Background(), dc.Name, r.ID, &govultr.DomainRecordReq{r.Name, r.Type, r.Data, r.TTL, &r.Priority})
}, },
}) })
} }
@@ -154,24 +173,38 @@ func (api *vultrProvider) EnsureDomainExists(domain string) error {
} }
// Vultr requires an initial IP, use a dummy one. // Vultr requires an initial IP, use a dummy one.
return api.client.DNSDomain.Create(context.Background(), domain, "0.0.0.0") _, err := api.client.Domain.Create(context.Background(), &govultr.DomainReq{domain, "0.0.0.0", "disabled"})
return err
} }
func (api *vultrProvider) isDomainInAccount(domain string) (bool, error) { func (api *vultrProvider) isDomainInAccount(domain string) (bool, error) {
domains, err := api.client.DNSDomain.List(context.Background()) listOptions := &govultr.ListOptions{}
if err != nil { domains, meta, err := api.client.Domain.List(context.Background(), listOptions)
return false, err
} for {
for _, d := range domains { if err != nil {
if d.Domain == domain { return false, err
return true, nil }
for _, d := range domains {
if d.Domain == domain {
return true, nil
}
}
if meta.Links.Next == "" {
break
} else {
listOptions.Cursor = meta.Links.Next
domains, meta, err = api.client.Domain.List(context.Background(), listOptions)
continue
} }
} }
return false, nil return false, nil
} }
// toRecordConfig converts a Vultr DNSRecord to a RecordConfig. #rtype_variations // toRecordConfig converts a Vultr DomainRecord to a RecordConfig. #rtype_variations
func toRecordConfig(domain string, r *govultr.DNSRecord) (*models.RecordConfig, error) { func toRecordConfig(domain string, r *govultr.DomainRecord) (*models.RecordConfig, error) {
origin, data := domain, r.Data origin, data := domain, r.Data
rc := &models.RecordConfig{ rc := &models.RecordConfig{
TTL: uint32(r.TTL), TTL: uint32(r.TTL),
@@ -194,10 +227,10 @@ func toRecordConfig(domain string, r *govultr.DNSRecord) (*models.RecordConfig,
if !strings.HasSuffix(data, ".") { if !strings.HasSuffix(data, ".") {
data = data + "." data = data + "."
} }
return rc, rc.SetTargetMX(uint16(vultrPriority(r)), data) return rc, rc.SetTargetMX(uint16(r.Priority), data)
case "SRV": case "SRV":
// Vultr returns SRV records in the format "[weight] [port] [target]". // Vultr returns SRV records in the format "[weight] [port] [target]".
return rc, rc.SetTargetSRVPriorityString(uint16(vultrPriority(r)), data) return rc, rc.SetTargetSRVPriorityString(uint16(r.Priority), data)
case "TXT": case "TXT":
// TXT records from Vultr are always surrounded by quotes. // TXT records from Vultr are always surrounded by quotes.
// They don't permit quotes within the string, therefore there is no // They don't permit quotes within the string, therefore there is no
@@ -213,8 +246,8 @@ func toRecordConfig(domain string, r *govultr.DNSRecord) (*models.RecordConfig,
} }
} }
// toVultrRecord converts a RecordConfig converted by toRecordConfig back to a Vultr DNSRecord. #rtype_variations // toVultrRecord converts a RecordConfig converted by toRecordConfig back to a Vultr DomainRecordReq. #rtype_variations
func toVultrRecord(dc *models.DomainConfig, rc *models.RecordConfig, vultrID int) *govultr.DNSRecord { func toVultrRecord(dc *models.DomainConfig, rc *models.RecordConfig, vultrID string) *govultr.DomainRecord {
name := rc.GetLabel() name := rc.GetLabel()
// Vultr uses a blank string to represent the apex domain. // Vultr uses a blank string to represent the apex domain.
if name == "@" { if name == "@" {
@@ -226,19 +259,17 @@ func toVultrRecord(dc *models.DomainConfig, rc *models.RecordConfig, vultrID int
// Vultr does not use a period suffix for CNAME, NS, or MX. // Vultr does not use a period suffix for CNAME, NS, or MX.
data = strings.TrimSuffix(data, ".") data = strings.TrimSuffix(data, ".")
var priority *int priority := 0
if rc.Type == "MX" { if rc.Type == "MX" {
tmp := int(rc.MxPreference) priority = int(rc.MxPreference)
priority = &tmp
} }
if rc.Type == "SRV" { if rc.Type == "SRV" {
tmp := int(rc.SrvPriority) priority = int(rc.SrvPriority)
priority = &tmp
} }
r := &govultr.DNSRecord{ r := &govultr.DomainRecord{
RecordID: vultrID, ID: vultrID,
Type: rc.Type, Type: rc.Type,
Name: name, Name: name,
Data: data, Data: data,
@@ -262,10 +293,3 @@ func toVultrRecord(dc *models.DomainConfig, rc *models.RecordConfig, vultrID int
return r return r
} }
func vultrPriority(r *govultr.DNSRecord) int {
if r.Priority == nil {
return 0
}
return *r.Priority
}