From 2898fbad727dac3abe8a9d3505ff30be400a19e4 Mon Sep 17 00:00:00 2001 From: Nicolas Lorin Date: Thu, 13 Oct 2022 00:17:15 +0200 Subject: [PATCH] VULTR: use API v2 (#1768) --- go.mod | 2 +- go.sum | 4 +- providers/vultr/convert_test.go | 12 ++-- providers/vultr/vultrProvider.go | 118 +++++++++++++++++++------------ 4 files changed, 80 insertions(+), 56 deletions(-) diff --git a/go.mod b/go.mod index 497ebfe02..0d0c9008f 100644 --- a/go.mod +++ b/go.mod @@ -51,7 +51,6 @@ require ( github.com/tdewolff/minify/v2 v2.12.3 github.com/transip/gotransip/v6 v6.17.0 github.com/urfave/cli/v2 v2.17.1 - github.com/vultr/govultr v1.1.1 github.com/xddxdd/ottoext v0.0.0-20210101073831-439879ee6281 golang.org/x/net v0.0.0-20221002022538-bcab6841153b 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/stretchr/objx v0.4.0 // 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 go.opencensus.io v0.23.0 // indirect go.uber.org/atomic v1.9.0 // indirect diff --git a/go.sum b/go.sum index 87bbdaf24..aeb01bfd4 100644 --- a/go.sum +++ b/go.sum @@ -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/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/vultr/govultr v1.1.1 h1:ntltxMYyJstjKz1v2CLNxFZiqjlt/8HqD1GZeJ/fAMc= -github.com/vultr/govultr v1.1.1/go.mod h1:QXCNTRg0nwu95ayiMC3feYvrAFTLnj94s2FiibIpoC4= +github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs= +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/go.mod h1:8Hr+gV9GtucFQOptKfzuYxqg++CyIjg4E7QCR7F3Dxw= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= diff --git a/providers/vultr/convert_test.go b/providers/vultr/convert_test.go index 4da5b1ea0..e995e369a 100644 --- a/providers/vultr/convert_test.go +++ b/providers/vultr/convert_test.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/StackExchange/dnscontrol/v3/models" - "github.com/vultr/govultr" + "github.com/vultr/govultr/v2" ) func TestConversion(t *testing.T) { @@ -12,7 +12,7 @@ func TestConversion(t *testing.T) { Name: "example.com", } - records := []*govultr.DNSRecord{ + records := []*govultr.DomainRecord{ { Type: "A", Name: "", @@ -29,14 +29,14 @@ func TestConversion(t *testing.T) { Type: "SRV", Name: "_ssh_.tcp", Data: "5 22 ssh.example.com", - Priority: intPtr(5), + Priority: 5, TTL: 300, }, { Type: "MX", Name: "", Data: "mail.example.com", - Priority: intPtr(0), + Priority: 0, TTL: 300, }, { @@ -65,9 +65,9 @@ func TestConversion(t *testing.T) { 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) } } diff --git a/providers/vultr/vultrProvider.go b/providers/vultr/vultrProvider.go index 6244fc1c3..274384923 100644 --- a/providers/vultr/vultrProvider.go +++ b/providers/vultr/vultrProvider.go @@ -5,13 +5,14 @@ import ( "encoding/json" "errors" "fmt" - "strconv" "strings" + "golang.org/x/oauth2" + "github.com/StackExchange/dnscontrol/v3/models" "github.com/StackExchange/dnscontrol/v3/pkg/diff" "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") } - client := govultr.NewClient(nil, token) + config := &oauth2.Config{} + + client := govultr.NewClient(config.Client(context.Background(), &oauth2.Token{AccessToken: token})) client.SetUserAgent("dnscontrol") - _, err := client.Account.GetInfo(context.Background()) + _, err := client.Account.Get(context.Background()) return &vultrProvider{client, token}, err } // GetZoneRecords gets the records of a zone and returns them in RecordConfig format. func (api *vultrProvider) GetZoneRecords(domain string) (models.Records, error) { - records, err := api.client.DNSRecord.List(context.Background(), domain) - if err != nil { - return nil, err - } + listOptions := &govultr.ListOptions{} + records, meta, err := api.client.DomainRecord.List(context.Background(), domain, listOptions) + curRecords := make(models.Records, meta.Total) + nextI := 0 - curRecords := make(models.Records, len(records)) - for i := range records { - r, err := toRecordConfig(domain, &records[i]) + for { if err != nil { 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 @@ -108,31 +126,32 @@ func (api *vultrProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*mode var corrections []*models.Correction for _, mod := range delete { - id := mod.Existing.Original.(*govultr.DNSRecord).RecordID + id := mod.Existing.Original.(*govultr.DomainRecord).ID corrections = append(corrections, &models.Correction{ Msg: fmt.Sprintf("%s; Vultr RecordID: %v", mod.String(), id), 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 { - r := toVultrRecord(dc, mod.Desired, 0) + r := toVultrRecord(dc, mod.Desired, "0") corrections = append(corrections, &models.Correction{ Msg: mod.String(), 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 { - 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{ - 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 { - 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. - 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) { - domains, err := api.client.DNSDomain.List(context.Background()) - if err != nil { - return false, err - } - for _, d := range domains { - if d.Domain == domain { - return true, nil + listOptions := &govultr.ListOptions{} + domains, meta, err := api.client.Domain.List(context.Background(), listOptions) + + for { + if err != nil { + return false, err + } + + 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 } -// toRecordConfig converts a Vultr DNSRecord to a RecordConfig. #rtype_variations -func toRecordConfig(domain string, r *govultr.DNSRecord) (*models.RecordConfig, error) { +// toRecordConfig converts a Vultr DomainRecord to a RecordConfig. #rtype_variations +func toRecordConfig(domain string, r *govultr.DomainRecord) (*models.RecordConfig, error) { origin, data := domain, r.Data rc := &models.RecordConfig{ TTL: uint32(r.TTL), @@ -194,10 +227,10 @@ func toRecordConfig(domain string, r *govultr.DNSRecord) (*models.RecordConfig, if !strings.HasSuffix(data, ".") { data = data + "." } - return rc, rc.SetTargetMX(uint16(vultrPriority(r)), data) + return rc, rc.SetTargetMX(uint16(r.Priority), data) case "SRV": // 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": // TXT records from Vultr are always surrounded by quotes. // 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 -func toVultrRecord(dc *models.DomainConfig, rc *models.RecordConfig, vultrID int) *govultr.DNSRecord { +// toVultrRecord converts a RecordConfig converted by toRecordConfig back to a Vultr DomainRecordReq. #rtype_variations +func toVultrRecord(dc *models.DomainConfig, rc *models.RecordConfig, vultrID string) *govultr.DomainRecord { name := rc.GetLabel() // Vultr uses a blank string to represent the apex domain. 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. data = strings.TrimSuffix(data, ".") - var priority *int + priority := 0 if rc.Type == "MX" { - tmp := int(rc.MxPreference) - priority = &tmp + priority = int(rc.MxPreference) } if rc.Type == "SRV" { - tmp := int(rc.SrvPriority) - priority = &tmp + priority = int(rc.SrvPriority) } - r := &govultr.DNSRecord{ - RecordID: vultrID, + r := &govultr.DomainRecord{ + ID: vultrID, Type: rc.Type, Name: name, Data: data, @@ -262,10 +293,3 @@ func toVultrRecord(dc *models.DomainConfig, rc *models.RecordConfig, vultrID int return r } - -func vultrPriority(r *govultr.DNSRecord) int { - if r.Priority == nil { - return 0 - } - return *r.Priority -}