Skip to content

Commit a8e19ef

Browse files
authored
chore: replace official Cloudflare API client by an internal API client (#2583)
1 parent b0e3fd2 commit a8e19ef

File tree

15 files changed

+826
-44
lines changed

15 files changed

+826
-44
lines changed

go.mod

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ require (
3030
github.com/baidubce/bce-sdk-go v0.9.223
3131
github.com/cenkalti/backoff/v4 v4.3.0
3232
github.com/civo/civogo v0.3.11
33-
github.com/cloudflare/cloudflare-go v0.115.0
3433
github.com/dnsimple/dnsimple-go/v4 v4.0.0
3534
github.com/exoscale/egoscale/v3 v3.1.13
3635
github.com/go-jose/go-jose/v4 v4.0.5
@@ -148,7 +147,6 @@ require (
148147
github.com/go-playground/universal-translator v0.18.1 // indirect
149148
github.com/go-playground/validator/v10 v10.16.0 // indirect
150149
github.com/go-resty/resty/v2 v2.16.5 // indirect
151-
github.com/goccy/go-json v0.10.5 // indirect
152150
github.com/gofrs/flock v0.12.1 // indirect
153151
github.com/golang-jwt/jwt/v4 v4.5.1 // indirect
154152
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect

go.sum

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -249,8 +249,6 @@ github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5P
249249
github.com/clbanning/mxj/v2 v2.5.5 h1:oT81vUeEiQQ/DcHbzSytRngP6Ky9O+L+0Bw0zSJag9E=
250250
github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
251251
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
252-
github.com/cloudflare/cloudflare-go v0.115.0 h1:84/dxeeXweCc0PN5Cto44iTA8AkG1fyT11yPO5ZB7sM=
253-
github.com/cloudflare/cloudflare-go v0.115.0/go.mod h1:Ds6urDwn/TF2uIU24mu7H91xkKP8gSAHxQ44DSZgVmU=
254252
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
255253
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
256254
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
@@ -359,8 +357,6 @@ github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlnd
359357
github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw=
360358
github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b h1:/vQ+oYKu+JoyaMPDsv5FzwuL2wwWBgBbtj/YLCi4LuA=
361359
github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b/go.mod h1:Xo4aNUOrJnVruqWQJBtW6+bTBDTniY8yZum5rF3b5jw=
362-
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
363-
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
364360
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
365361
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
366362
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=

platform/tester/servermock/link_request_body_json.go

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@ import (
1414

1515
// RequestBodyJSONLink validates JSON request bodies.
1616
type RequestBodyJSONLink struct {
17-
body []byte
18-
filename string
19-
data any
17+
body []byte
18+
filename string
19+
directory string
20+
data any
2021
}
2122

2223
// CheckRequestJSONBody creates a [RequestBodyJSONLink] initialized with a string.
@@ -31,7 +32,10 @@ func CheckRequestJSONBodyFromStruct(data any) *RequestBodyJSONLink {
3132

3233
// CheckRequestJSONBodyFromFile creates a [RequestBodyJSONLink] initialized with the provided request body file.
3334
func CheckRequestJSONBodyFromFile(filename string) *RequestBodyJSONLink {
34-
return &RequestBodyJSONLink{filename: filename}
35+
return &RequestBodyJSONLink{
36+
filename: filename,
37+
directory: "fixtures",
38+
}
3539
}
3640

3741
func (l *RequestBodyJSONLink) Bind(next http.Handler) http.Handler {
@@ -55,7 +59,7 @@ func (l *RequestBodyJSONLink) Bind(next http.Handler) http.Handler {
5559

5660
switch {
5761
case l.filename != "":
58-
expectedRaw, err = os.ReadFile(filepath.Join("fixtures", l.filename))
62+
expectedRaw, err = os.ReadFile(filepath.Join(l.directory, l.filename))
5963
if err != nil {
6064
http.Error(rw, err.Error(), http.StatusInternalServerError)
6165
return
@@ -97,3 +101,9 @@ func (l *RequestBodyJSONLink) Bind(next http.Handler) http.Handler {
97101
next.ServeHTTP(rw, req)
98102
})
99103
}
104+
105+
func (l *RequestBodyJSONLink) WithDirectory(directory string) *RequestBodyJSONLink {
106+
l.directory = directory
107+
108+
return l
109+
}

providers/dns/cloudflare/cloudflare.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ import (
1111
"sync"
1212
"time"
1313

14-
"github.com/cloudflare/cloudflare-go"
1514
"github.com/go-acme/lego/v4/challenge"
1615
"github.com/go-acme/lego/v4/challenge/dns01"
1716
"github.com/go-acme/lego/v4/log"
1817
"github.com/go-acme/lego/v4/platform/config/env"
18+
"github.com/go-acme/lego/v4/providers/dns/cloudflare/internal"
1919
)
2020

2121
// Environment variables names.
@@ -156,24 +156,26 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
156156
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
157157
info := dns01.GetChallengeInfo(domain, keyAuth)
158158

159+
ctx := context.Background()
160+
159161
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
160162
if err != nil {
161163
return fmt.Errorf("cloudflare: could not find zone for domain %q: %w", domain, err)
162164
}
163165

164-
zoneID, err := d.client.ZoneIDByName(authZone)
166+
zoneID, err := d.client.ZoneIDByName(ctx, authZone)
165167
if err != nil {
166168
return fmt.Errorf("cloudflare: failed to find zone %s: %w", authZone, err)
167169
}
168170

169-
dnsRecord := cloudflare.CreateDNSRecordParams{
171+
dnsRecord := internal.Record{
170172
Type: "TXT",
171173
Name: dns01.UnFqdn(info.EffectiveFQDN),
172174
Content: `"` + info.Value + `"`,
173175
TTL: d.config.TTL,
174176
}
175177

176-
response, err := d.client.CreateDNSRecord(context.Background(), zoneID, dnsRecord)
178+
response, err := d.client.CreateDNSRecord(ctx, zoneID, dnsRecord)
177179
if err != nil {
178180
return fmt.Errorf("cloudflare: failed to create TXT record: %w", err)
179181
}
@@ -196,7 +198,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
196198
return fmt.Errorf("cloudflare: could not find zone for domain %q: %w", domain, err)
197199
}
198200

199-
zoneID, err := d.client.ZoneIDByName(authZone)
201+
zoneID, err := d.client.ZoneIDByName(context.Background(), authZone)
200202
if err != nil {
201203
return fmt.Errorf("cloudflare: failed to find zone %s: %w", authZone, err)
202204
}

providers/dns/cloudflare/cloudflare_test.go

Lines changed: 71 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
package cloudflare
22

33
import (
4+
"net/http/httptest"
5+
"path/filepath"
46
"testing"
57
"time"
68

79
"github.com/go-acme/lego/v4/platform/tester"
10+
"github.com/go-acme/lego/v4/platform/tester/servermock"
811
"github.com/stretchr/testify/assert"
912
"github.com/stretchr/testify/require"
1013
)
@@ -232,22 +235,17 @@ func TestNewDNSProviderConfig(t *testing.T) {
232235
},
233236
{
234237
desc: "missing credentials",
235-
expected: "cloudflare: invalid credentials: key & email must not be empty",
238+
expected: "cloudflare: invalid credentials: authEmail, authKey or authToken must be set",
236239
},
237240
{
238241
desc: "missing email",
239242
authKey: "123",
240-
expected: "cloudflare: invalid credentials: key & email must not be empty",
243+
expected: "cloudflare: invalid credentials: authEmail and authKey must be set together",
241244
},
242245
{
243246
desc: "missing api key",
244247
authEmail: "[email protected]",
245-
expected: "cloudflare: invalid credentials: key & email must not be empty",
246-
},
247-
{
248-
desc: "missing api token, fallback to api key/email",
249-
authToken: "",
250-
expected: "cloudflare: invalid credentials: key & email must not be empty",
248+
expected: "cloudflare: invalid credentials: authEmail and authKey must be set together",
251249
},
252250
}
253251

@@ -299,3 +297,68 @@ func TestLiveCleanUp(t *testing.T) {
299297
err = provider.CleanUp(envTest.GetDomain(), "", "123d==")
300298
require.NoError(t, err)
301299
}
300+
301+
func mockBuilder() *servermock.Builder[*DNSProvider] {
302+
return servermock.NewBuilder(
303+
func(server *httptest.Server) (*DNSProvider, error) {
304+
config := NewDefaultConfig()
305+
config.AuthEmail = "[email protected]"
306+
config.AuthKey = "secret"
307+
config.BaseURL = server.URL
308+
309+
return NewDNSProviderConfig(config)
310+
},
311+
servermock.CheckHeader().
312+
WithRegexp("User-Agent", `goacme-lego/[0-9.]+ \(.+\)`).
313+
With("X-Auth-Email", "[email protected]").
314+
With("X-Auth-Key", "secret"),
315+
)
316+
}
317+
318+
func TestDNSProvider_Present(t *testing.T) {
319+
provider := mockBuilder().
320+
// https://developers.cloudflare.com/api/resources/zones/methods/list/
321+
Route("GET /zones",
322+
responseFromFixture("zones.json"),
323+
servermock.CheckQueryParameter().Strict().
324+
With("name", "example.com").
325+
With("per_page", "50")).
326+
// https://developers.cloudflare.com/api/resources/dns/subresources/records/methods/create/
327+
Route("POST /zones/023e105f4ecef8ad9ca31a8372d0c353/dns_records",
328+
responseFromFixture("create_record.json"),
329+
servermock.CheckHeader().
330+
WithContentType("application/json"),
331+
servermock.CheckRequestJSONBodyFromFile("create_record-request.json").
332+
WithDirectory(filepath.Join("internal", "fixtures"))).
333+
Build(t)
334+
335+
err := provider.Present("example.com", "abc", "123d==")
336+
require.NoError(t, err)
337+
}
338+
339+
func TestDNSProvider_CleanUp(t *testing.T) {
340+
provider := mockBuilder().
341+
// https://developers.cloudflare.com/api/resources/zones/methods/list/
342+
Route("GET /zones",
343+
responseFromFixture("zones.json"),
344+
servermock.CheckQueryParameter().Strict().
345+
With("name", "example.com").
346+
With("per_page", "50")).
347+
// https://developers.cloudflare.com/api/resources/dns/subresources/records/methods/delete/
348+
Route("DELETE /zones/023e105f4ecef8ad9ca31a8372d0c353/dns_records/xxx",
349+
responseFromFixture("delete_record.json")).
350+
Build(t)
351+
352+
token := "abc"
353+
354+
provider.recordIDsMu.Lock()
355+
provider.recordIDs["abc"] = "xxx"
356+
provider.recordIDsMu.Unlock()
357+
358+
err := provider.CleanUp("example.com", token, "123d==")
359+
require.NoError(t, err)
360+
}
361+
362+
func responseFromFixture(filename string) *servermock.ResponseFromFileHandler {
363+
return servermock.ResponseFromFile(filepath.Join("internal", "fixtures", filename))
364+
}

0 commit comments

Comments
 (0)