Compare commits
No commits in common. "v2.0.5" and "master" have entirely different histories.
107
client.go
107
client.go
@ -3,7 +3,6 @@ package vultr
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
@ -12,15 +11,8 @@ import (
|
||||
"github.com/vultr/govultr/v3"
|
||||
)
|
||||
|
||||
// vultrDuplicateErr is matched against Vultr API response text.
|
||||
// May need updating if Vultr changes their error message wording.
|
||||
const vultrDuplicateErr = "Duplicate records"
|
||||
|
||||
type Client struct {
|
||||
vultr *govultr.Client
|
||||
// mutex serializes write operations (add/remove/update).
|
||||
// getDNSEntries does NOT acquire this lock because it is called
|
||||
// from within write methods that already hold it.
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
@ -72,41 +64,9 @@ func (p *Provider) addDNSRecord(ctx context.Context, domain string, r libdns.Rec
|
||||
|
||||
rec, _, err := p.client.vultr.DomainRecord.Create(ctx, domain, &domainRecordReq)
|
||||
if err != nil {
|
||||
if !strings.Contains(err.Error(), vultrDuplicateErr) {
|
||||
return r, err
|
||||
}
|
||||
|
||||
// Duplicate record exists — find it and update with the new data.
|
||||
// This handles stale ACME challenge TXT records left from previous runs.
|
||||
records, lookupErr := p.getDNSEntries(ctx, domain)
|
||||
if lookupErr != nil {
|
||||
return r, fmt.Errorf("duplicate record and could not look up existing: %w", lookupErr)
|
||||
}
|
||||
|
||||
rr := r.RR()
|
||||
var existingID string
|
||||
for _, existing := range records {
|
||||
exRR := existing.RR()
|
||||
if exRR.Name == rr.Name && exRR.Type == rr.Type {
|
||||
if vr, ok := existing.(VultrRecord); ok {
|
||||
existingID = vr.ID
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if existingID == "" {
|
||||
return r, fmt.Errorf("duplicate record reported but not found in zone: %w", err)
|
||||
}
|
||||
|
||||
updateErr := p.client.vultr.DomainRecord.Update(ctx, domain, existingID, &domainRecordReq)
|
||||
if updateErr != nil {
|
||||
return r, fmt.Errorf("failed to update duplicate record %s: %w", existingID, updateErr)
|
||||
}
|
||||
|
||||
return fromLibdnsRecord(r, existingID), nil
|
||||
}
|
||||
|
||||
record := fromLibdnsRecord(r, rec.ID)
|
||||
|
||||
return record, nil
|
||||
@ -121,42 +81,18 @@ func (p *Provider) removeDNSRecord(ctx context.Context, domain string, record li
|
||||
recordId, err := getRecordId(record)
|
||||
if err != nil {
|
||||
// try to get the ID from API if we don't have it
|
||||
records, lookupErr := p.getDNSEntries(ctx, domain)
|
||||
if lookupErr != nil {
|
||||
return record, fmt.Errorf("could not get record ID from API: %w", lookupErr)
|
||||
records, err := p.getDNSEntries(ctx, domain)
|
||||
if err != nil {
|
||||
return record, fmt.Errorf("could not get record ID from API")
|
||||
}
|
||||
|
||||
rr := record.RR()
|
||||
for _, rec := range records {
|
||||
recRR := rec.RR()
|
||||
// Match by name, type, AND data to avoid deleting the wrong record
|
||||
// when multiple records share the same name (e.g. _acme-challenge TXT).
|
||||
if recRR.Name == rr.Name && recRR.Type == rr.Type && recRR.Data == rr.Data {
|
||||
if vr, ok := rec.(VultrRecord); ok {
|
||||
recordId = vr.ID
|
||||
break
|
||||
if rec.RR().Name == record.RR().Name {
|
||||
recordId = rec.(VultrRecord).ID
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to name+type match if exact data match wasn't found
|
||||
if recordId == "" {
|
||||
for _, rec := range records {
|
||||
recRR := rec.RR()
|
||||
if recRR.Name == rr.Name && recRR.Type == rr.Type {
|
||||
if vr, ok := rec.(VultrRecord); ok {
|
||||
recordId = vr.ID
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if recordId == "" {
|
||||
return record, fmt.Errorf("no matching record found for %s %s in zone %s", rr.Type, rr.Name, domain)
|
||||
}
|
||||
}
|
||||
|
||||
err = p.client.vultr.DomainRecord.Delete(ctx, domain, recordId)
|
||||
if err != nil {
|
||||
return record, err
|
||||
@ -174,41 +110,18 @@ func (p *Provider) updateDNSRecord(ctx context.Context, domain string, record li
|
||||
recordId, err := getRecordId(record)
|
||||
if err != nil {
|
||||
// try to get the ID from API if we don't have it
|
||||
records, lookupErr := p.getDNSEntries(ctx, domain)
|
||||
if lookupErr != nil {
|
||||
return record, fmt.Errorf("could not get record ID from API: %w", lookupErr)
|
||||
records, err := p.getDNSEntries(ctx, domain)
|
||||
if err != nil {
|
||||
return record, fmt.Errorf("could not get record ID from API")
|
||||
}
|
||||
|
||||
rr := record.RR()
|
||||
for _, rec := range records {
|
||||
recRR := rec.RR()
|
||||
// Match by name+type+data first for precision
|
||||
if recRR.Name == rr.Name && recRR.Type == rr.Type && recRR.Data == rr.Data {
|
||||
if vr, ok := rec.(VultrRecord); ok {
|
||||
recordId = vr.ID
|
||||
break
|
||||
if rec.RR().Data == record.RR().Data {
|
||||
recordId = rec.(VultrRecord).ID
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to name+type if exact data match not found
|
||||
if recordId == "" {
|
||||
for _, rec := range records {
|
||||
recRR := rec.RR()
|
||||
if recRR.Name == rr.Name && recRR.Type == rr.Type {
|
||||
if vr, ok := rec.(VultrRecord); ok {
|
||||
recordId = vr.ID
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if recordId == "" {
|
||||
return record, fmt.Errorf("no matching record found for %s %s in zone %s", rr.Type, rr.Name, domain)
|
||||
}
|
||||
}
|
||||
|
||||
domainRecordReq := toDomainRecordReq(record)
|
||||
|
||||
err = p.client.vultr.DomainRecord.Update(ctx, domain, recordId, &domainRecordReq)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user