Skip to content

Commit e57c132

Browse files
authored
Upgrade to libdns v1 beta APIs (#15)
1 parent f0aea7c commit e57c132

File tree

5 files changed

+104
-106
lines changed

5 files changed

+104
-106
lines changed

client.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,13 @@ func (p *Provider) updateRecord(ctx context.Context, oldRec, newRec cfDNSRecord)
5959
}
6060

6161
func (p *Provider) getDNSRecords(ctx context.Context, zoneInfo cfZone, rec libdns.Record, matchContent bool) ([]cfDNSRecord, error) {
62+
rr := rec.RR()
63+
6264
qs := make(url.Values)
63-
qs.Set("type", rec.Type)
64-
qs.Set("name", libdns.AbsoluteName(rec.Name, zoneInfo.Name))
65+
qs.Set("type", rr.Type)
66+
qs.Set("name", libdns.AbsoluteName(rr.Name, zoneInfo.Name))
6567
if matchContent {
66-
qs.Set("content", rec.Value)
68+
qs.Set("content", rr.Data)
6769
}
6870

6971
reqURL := fmt.Sprintf("%s/zones/%s/dns_records?%s", baseURL, zoneInfo.ID, qs.Encode())
@@ -121,7 +123,7 @@ func (p *Provider) getZoneInfo(ctx context.Context, zoneName string) (cfZone, er
121123
// error including error information from the API if applicable. If result is a
122124
// non-nil pointer, the result field from the API response will be decoded into
123125
// it for convenience.
124-
func (p *Provider) doAPIRequest(req *http.Request, result interface{}) (cfResponse, error) {
126+
func (p *Provider) doAPIRequest(req *http.Request, result any) (cfResponse, error) {
125127
if req.Header.Get("Authorization") == "" {
126128
req.Header.Set("Authorization", "Bearer "+p.APIToken)
127129
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ module github.com/libdns/cloudflare
22

33
go 1.18
44

5-
require github.com/libdns/libdns v0.2.3
5+
require github.com/libdns/libdns v1.0.0-beta.1

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
github.com/libdns/libdns v0.2.3 h1:ba30K4ObwMGB/QTmqUxf3H4/GmUrCAIkMWejeGl12v8=
2-
github.com/libdns/libdns v0.2.3/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
1+
github.com/libdns/libdns v1.0.0-beta.1 h1:KIf4wLfsrEpXpZ3vmc/poM8zCATXT2klbdPe6hyOBjQ=
2+
github.com/libdns/libdns v1.0.0-beta.1/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=

models.go

Lines changed: 47 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,9 @@ type cfDNSRecord struct {
8181
Service string `json:"service,omitempty"`
8282
Proto string `json:"proto,omitempty"`
8383
Name string `json:"name,omitempty"`
84-
Priority uint `json:"priority,omitempty"`
85-
Weight uint `json:"weight,omitempty"`
86-
Port uint `json:"port,omitempty"`
84+
Priority uint16 `json:"priority,omitempty"`
85+
Weight uint16 `json:"weight,omitempty"`
86+
Port uint16 `json:"port,omitempty"`
8787
Target string `json:"target,omitempty"`
8888
Value string `json:"value,omitempty"`
8989

@@ -110,67 +110,56 @@ type cfDNSRecord struct {
110110
} `json:"meta,omitempty"`
111111
}
112112

113-
func (r cfDNSRecord) libdnsRecord(zone string) libdns.Record {
114-
if r.Type == "SRV" {
115-
srv := libdns.SRV{
116-
Service: strings.TrimPrefix(r.Data.Service, "_"),
117-
Proto: strings.TrimPrefix(r.Data.Proto, "_"),
118-
Name: r.Data.Name,
119-
Priority: r.Data.Priority,
120-
Weight: r.Data.Weight,
121-
Port: r.Data.Port,
122-
Target: r.Data.Target,
123-
}
124-
return srv.ToRecord()
125-
}
126-
rec := libdns.Record{
127-
Type: r.Type,
128-
Name: libdns.RelativeName(r.Name, zone),
129-
Value: r.Content,
130-
TTL: time.Duration(r.TTL) * time.Second,
131-
ID: r.ID,
132-
}
133-
if r.Type == "HTTPS" {
134-
rec.Value = r.Data.Value
135-
rec.Priority = r.Data.Priority
136-
rec.Target = r.Data.Target
137-
}
138-
return rec
113+
func (r cfDNSRecord) libdnsRecord(zone string) (libdns.Record, error) {
114+
return libdns.RR{
115+
Name: libdns.RelativeName(r.Name, zone),
116+
TTL: time.Duration(r.TTL) * time.Second,
117+
Type: r.Type,
118+
Data: r.Content,
119+
}.Parse()
139120
}
140121

141122
func cloudflareRecord(r libdns.Record) (cfDNSRecord, error) {
142-
rec := cfDNSRecord{
143-
ID: r.ID,
144-
Type: r.Type,
145-
TTL: int(r.TTL.Seconds()),
123+
// Super annoyingly, the Cloudflare API says that a "Content"
124+
// field can contain the record data as a string, and that the
125+
// individual component fields are optional (this would be
126+
// ideal so we don't have to parse every single record type
127+
// into a separate struct, we can just submit the Content
128+
// string like what the RR struct has for us); yet when I try
129+
// to submit records using the Content field, I get errors
130+
// saying that the individual data components are required,
131+
// despite the docs saying they're optional.
132+
// So, instead of a 5-line function, we have a much bigger
133+
// more complicated and error prone function here.
134+
// And of course there's no real good venue to file a bug report:
135+
// https://community.cloudflare.com/t/creating-srv-record-with-content-string-instead-of-individual-component-fields/781178?u=mholt
136+
rr := r.RR()
137+
cfRec := cfDNSRecord{
138+
// ID: r.ID,
139+
Name: rr.Name,
140+
Type: rr.Type,
141+
TTL: int(rr.TTL.Seconds()),
142+
Content: rr.Data,
146143
}
147-
switch r.Type {
148-
case "SRV":
149-
srv, err := r.ToSRV()
150-
if err != nil {
151-
return cfDNSRecord{}, err
152-
}
153-
rec.Data.Service = "_" + srv.Service
154-
rec.Data.Priority = srv.Priority
155-
rec.Data.Weight = srv.Weight
156-
rec.Data.Proto = "_" + srv.Proto
157-
rec.Data.Name = srv.Name
158-
rec.Data.Port = srv.Port
159-
rec.Data.Target = srv.Target
160-
case "HTTPS":
161-
rec.Name = r.Name
162-
rec.Data.Priority = r.Priority
163-
rec.Data.Target = r.Target
164-
rec.Data.Value = r.Value
165-
default:
166-
rec.Name = r.Name
167-
rec.Data.Priority = r.Priority
168-
rec.Content = r.Value
144+
switch rec := r.(type) {
145+
case libdns.SRV:
146+
cfRec.Data.Service = "_" + rec.Service
147+
cfRec.Data.Priority = rec.Priority
148+
cfRec.Data.Weight = rec.Weight
149+
cfRec.Data.Proto = "_" + rec.Transport
150+
cfRec.Data.Name = rec.Name
151+
cfRec.Data.Port = rec.Port
152+
cfRec.Data.Target = rec.Target
153+
case libdns.ServiceBinding:
154+
cfRec.Name = rec.Name
155+
cfRec.Data.Priority = rec.Priority
156+
cfRec.Data.Target = rec.Target
157+
cfRec.Data.Value = rec.Params.String()
169158
}
170-
if rec.Type == "CNAME" && strings.HasSuffix(rec.Content, ".cfargotunnel.com") {
171-
rec.Proxied = true
159+
if rr.Type == "CNAME" && strings.HasSuffix(cfRec.Content, ".cfargotunnel.com") {
160+
cfRec.Proxied = true
172161
}
173-
return rec, nil
162+
return cfRec, nil
174163
}
175164

176165
// All API responses have this structure.

provider.go

Lines changed: 48 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package cloudflare
33
import (
44
"context"
55
"fmt"
6+
"log"
67
"net/http"
78
"sync"
89

@@ -42,8 +43,13 @@ func (p *Provider) GetRecords(ctx context.Context, zone string) ([]libdns.Record
4243

4344
recs := make([]libdns.Record, 0, len(result))
4445
for _, rec := range result {
45-
recs = append(recs, rec.libdnsRecord(zone))
46+
libdnsRec, err := rec.libdnsRecord(zone)
47+
if err != nil {
48+
return nil, fmt.Errorf("parsing Cloudflare DNS record %+v: %v", rec, err)
49+
}
50+
recs = append(recs, libdnsRec)
4651
}
52+
log.Printf("GOT RECORDS: %#v", recs)
4753

4854
return recs, nil
4955
}
@@ -61,7 +67,11 @@ func (p *Provider) AppendRecords(ctx context.Context, zone string, records []lib
6167
if err != nil {
6268
return nil, err
6369
}
64-
created = append(created, result.libdnsRecord(zone))
70+
libdnsRec, err := result.libdnsRecord(zone)
71+
if err != nil {
72+
return nil, fmt.Errorf("parsing Cloudflare DNS record %+v: %v", rec, err)
73+
}
74+
created = append(created, libdnsRec)
6575
}
6676

6777
return created, nil
@@ -77,29 +87,14 @@ func (p *Provider) DeleteRecords(ctx context.Context, zone string, records []lib
7787

7888
var recs []libdns.Record
7989
for _, rec := range records {
80-
// we create a "delete queue" for each record
81-
// requested for deletion; if the record ID
82-
// is known, that is the only one to fill the
83-
// queue, but if it's not known, we try to find
84-
// a match theoretically there could be more
85-
// than one
86-
var deleteQueue []libdns.Record
87-
88-
if rec.ID == "" {
89-
// record ID is required; try to find it with what was provided
90-
exactMatches, err := p.getDNSRecords(ctx, zoneInfo, rec, true)
91-
if err != nil {
92-
return nil, err
93-
}
94-
for _, rec := range exactMatches {
95-
deleteQueue = append(deleteQueue, rec.libdnsRecord(zone))
96-
}
97-
} else {
98-
deleteQueue = []libdns.Record{rec}
90+
// record ID is required; try to find it with what was provided
91+
exactMatches, err := p.getDNSRecords(ctx, zoneInfo, rec, true)
92+
if err != nil {
93+
return nil, err
9994
}
10095

101-
for _, delRec := range deleteQueue {
102-
reqURL := fmt.Sprintf("%s/zones/%s/dns_records/%s", baseURL, zoneInfo.ID, delRec.ID)
96+
for _, cfRec := range exactMatches {
97+
reqURL := fmt.Sprintf("%s/zones/%s/dns_records/%s", baseURL, zoneInfo.ID, cfRec.ID)
10398
req, err := http.NewRequestWithContext(ctx, "DELETE", reqURL, nil)
10499
if err != nil {
105100
return nil, err
@@ -111,7 +106,11 @@ func (p *Provider) DeleteRecords(ctx context.Context, zone string, records []lib
111106
return nil, err
112107
}
113108

114-
recs = append(recs, result.libdnsRecord(zone))
109+
libdnsRec, err := result.libdnsRecord(zone)
110+
if err != nil {
111+
return nil, fmt.Errorf("parsing Cloudflare DNS record %+v: %v", rec, err)
112+
}
113+
recs = append(recs, libdnsRec)
115114
}
116115

117116
}
@@ -134,27 +133,31 @@ func (p *Provider) SetRecords(ctx context.Context, zone string, records []libdns
134133
return nil, err
135134
}
136135
oldRec.ZoneID = zoneInfo.ID
137-
if rec.ID == "" {
138-
// the record might already exist, even if we don't know the ID yet
139-
matches, err := p.getDNSRecords(ctx, zoneInfo, rec, false)
136+
137+
// the record might already exist, even if we don't know the ID yet
138+
matches, err := p.getDNSRecords(ctx, zoneInfo, rec, false)
139+
if err != nil {
140+
return nil, err
141+
}
142+
if len(matches) == 0 {
143+
// record doesn't exist; create it
144+
result, err := p.createRecord(ctx, zoneInfo, rec)
140145
if err != nil {
141146
return nil, err
142147
}
143-
if len(matches) == 0 {
144-
// record doesn't exist; create it
145-
result, err := p.createRecord(ctx, zoneInfo, rec)
146-
if err != nil {
147-
return nil, err
148-
}
149-
results = append(results, result.libdnsRecord(zone))
150-
continue
151-
}
152-
if len(matches) > 1 {
153-
return nil, fmt.Errorf("unexpectedly found more than 1 record for %v", rec)
148+
libdnsRec, err := result.libdnsRecord(zone)
149+
if err != nil {
150+
return nil, fmt.Errorf("parsing Cloudflare DNS record %+v: %v", rec, err)
154151
}
155-
// record does exist, fill in the ID so that we can update it
156-
oldRec.ID = matches[0].ID
152+
results = append(results, libdnsRec)
153+
continue
154+
}
155+
if len(matches) > 1 {
156+
return nil, fmt.Errorf("unexpectedly found more than 1 record for %v", rec)
157157
}
158+
// record does exist, fill in the ID so that we can update it
159+
oldRec.ID = matches[0].ID
160+
158161
// record exists; update it
159162
cfRec, err := cloudflareRecord(rec)
160163
if err != nil {
@@ -164,7 +167,11 @@ func (p *Provider) SetRecords(ctx context.Context, zone string, records []libdns
164167
if err != nil {
165168
return nil, err
166169
}
167-
results = append(results, result.libdnsRecord(zone))
170+
libdnsRec, err := result.libdnsRecord(zone)
171+
if err != nil {
172+
return nil, fmt.Errorf("parsing Cloudflare DNS record %+v: %v", rec, err)
173+
}
174+
results = append(results, libdnsRec)
168175
}
169176

170177
return results, nil

0 commit comments

Comments
 (0)