Skip to content

Commit 2fc66c7

Browse files
authored
Merge pull request #11 from calebdoxsey/zoneIDs
support .co.uk domains
2 parents 06c77ad + d842044 commit 2fc66c7

File tree

6 files changed

+179
-94
lines changed

6 files changed

+179
-94
lines changed

Dockerfile

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM golang:1.11.2-alpine as build
1+
FROM golang:1.12-alpine as build
22
ENV GO111MODULE=on
33

44
RUN apk add --update --no-cache build-base git
@@ -12,10 +12,10 @@ WORKDIR /src
1212

1313

1414

15-
COPY go.mod .
15+
COPY go.mod ./
1616
RUN go mod download
1717

18-
COPY *.go .
18+
COPY *.go ./
1919
RUN CGO_ENABLED=0 go build -o /bin/kubernetes-cloudflare-sync .
2020

2121
FROM scratch
@@ -28,7 +28,7 @@ COPY --from=0 /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
2828
# practice.
2929
COPY --from=0 /etc_passwd /etc/passwd
3030

31-
COPY --from=build /bin/kubernetes-cloudflare-sync /bin/kubernetes-cloudflare-sync
31+
COPY --from=build /bin/kubernetes-cloudflare-sync /bin/kubernetes-cloudflare-sync
3232

3333
USER nobody
3434

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ require (
1919
github.com/pkg/errors v0.8.0
2020
github.com/pmezard/go-difflib v1.0.0 // indirect
2121
github.com/spf13/pflag v1.0.2 // indirect
22-
github.com/stretchr/testify v1.2.2 // indirect
22+
github.com/stretchr/testify v1.2.2
2323
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 // indirect
2424
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd // indirect
2525
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890 // indirect

go.sum

+1-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ k8s.io/api v0.0.0-20181221193117-173ce66c1e39 h1:iGq7zEPXFb0IeXAQK5RiYT1SVKX/af9
6565
k8s.io/api v0.0.0-20181221193117-173ce66c1e39/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
6666
k8s.io/apimachinery v0.0.0-20181108192626-90473842928c h1:cWEIJoXaA/PLmos29XffPIPWmxUcZKn1VhEEpZbp9p0=
6767
k8s.io/apimachinery v0.0.0-20181108192626-90473842928c/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0=
68-
k8s.io/client-go v9.0.0+incompatible h1:2kqW3X2xQ9SbFvWZjGEHBLlWc1LG9JIJNXWkuqwdZ3A=
68+
k8s.io/client-go v9.0.0+incompatible h1:/PdJjifJTjMFe0G4ESclZDcwF1+bFePTJ2xf+MXjcvs=
6969
k8s.io/client-go v9.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s=
7070
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
7171
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=

main.go

-88
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ import (
99
"strings"
1010
"time"
1111

12-
cloudflare "github.com/cloudflare/cloudflare-go"
13-
"github.com/pkg/errors"
1412
core_v1 "k8s.io/api/core/v1"
1513
"k8s.io/apimachinery/pkg/labels"
1614
"k8s.io/client-go/informers"
@@ -171,89 +169,3 @@ func nodeIsReady(node *core_v1.Node) bool {
171169

172170
return false
173171
}
174-
175-
func sync(ips []string, dnsNames []string, cloudflareTTL int, cloudflareProxy bool) error {
176-
api, err := cloudflare.New(options.CloudflareAPIKey, options.CloudflareAPIEmail)
177-
if err != nil {
178-
return errors.Wrap(err, "failed to access cloudflare api")
179-
}
180-
181-
root := dnsNames[0]
182-
for strings.Count(root, ".") > 1 {
183-
root = root[strings.Index(root, ".")+1:]
184-
}
185-
186-
zoneID, err := api.ZoneIDByName(root)
187-
if err != nil {
188-
return errors.Wrapf(err, "failed to find zone id for zone-name:=%s",
189-
root)
190-
}
191-
192-
known := map[string]bool{}
193-
for _, ip := range ips {
194-
known[ip] = true
195-
}
196-
197-
for _, dnsName := range dnsNames {
198-
records, err := api.DNSRecords(zoneID, cloudflare.DNSRecord{Type: "A", Name: dnsName})
199-
if err != nil {
200-
return errors.Wrapf(err, "failed to list dns records for zone-id=%s name=%s",
201-
zoneID, dnsName)
202-
}
203-
204-
seen := map[string]bool{}
205-
206-
for _, record := range records {
207-
log.Printf("found existing record name=%s ip=%s\n",
208-
record.Name, record.Content)
209-
if _, ok := known[record.Content]; ok {
210-
seen[record.Content] = true
211-
212-
if record.Proxied != cloudflareProxy || record.TTL != cloudflareTTL {
213-
log.Printf("updating dns record name=%s ip=%s\n",
214-
record.Name, record.Content)
215-
err := api.UpdateDNSRecord(zoneID, record.ID, cloudflare.DNSRecord{
216-
Type: record.Type,
217-
Name: record.Name,
218-
Content: record.Content,
219-
TTL: cloudflareTTL,
220-
Proxied: cloudflareProxy,
221-
})
222-
if err != nil {
223-
return errors.Wrapf(err, "failed to update dns record zone-id=%s record-id=%s name=%s ip=%s",
224-
zoneID, record.ID, record.Name, record.Content)
225-
}
226-
}
227-
} else {
228-
log.Printf("removing dns record name=%s ip=%s\n",
229-
record.Name, record.Content)
230-
err := api.DeleteDNSRecord(zoneID, record.ID)
231-
if err != nil {
232-
return errors.Wrapf(err, "failed to delete dns record zone-id=%s record-id=%s name=%s ip=%s",
233-
zoneID, record.ID, record.Name, record.Content)
234-
}
235-
}
236-
}
237-
238-
for ip := range known {
239-
if _, ok := seen[ip]; ok {
240-
continue
241-
}
242-
log.Printf("adding dns record name=%s ip=%s\n",
243-
dnsName, ip)
244-
_, err := api.CreateDNSRecord(zoneID, cloudflare.DNSRecord{
245-
Type: "A",
246-
Name: dnsName,
247-
Content: ip,
248-
TTL: cloudflareTTL,
249-
Proxied: cloudflareProxy,
250-
})
251-
if err != nil {
252-
return errors.Wrapf(err, "failed to create dns record zone-id=%s name=%s ip=%s",
253-
zoneID, dnsName, ip)
254-
}
255-
}
256-
}
257-
258-
return nil
259-
}

sync.go

+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package main
2+
3+
import (
4+
"log"
5+
"strings"
6+
7+
cloudflare "github.com/cloudflare/cloudflare-go"
8+
"github.com/pkg/errors"
9+
)
10+
11+
func sync(ips []string, dnsNames []string, cloudflareTTL int, cloudflareProxy bool) error {
12+
api, err := cloudflare.New(options.CloudflareAPIKey, options.CloudflareAPIEmail)
13+
if err != nil {
14+
return errors.Wrap(err, "failed to access cloudflare api")
15+
}
16+
17+
root := dnsNames[0]
18+
zoneID, err := findZoneID(api, root)
19+
if err != nil {
20+
return errors.Wrapf(err, "failed to find zone id for dns-name:=%s",
21+
root)
22+
}
23+
24+
known := map[string]bool{}
25+
for _, ip := range ips {
26+
known[ip] = true
27+
}
28+
29+
for _, dnsName := range dnsNames {
30+
records, err := api.DNSRecords(zoneID, cloudflare.DNSRecord{Type: "A", Name: dnsName})
31+
if err != nil {
32+
return errors.Wrapf(err, "failed to list dns records for zone-id=%s name=%s",
33+
zoneID, dnsName)
34+
}
35+
36+
seen := map[string]bool{}
37+
38+
for _, record := range records {
39+
log.Printf("found existing record name=%s ip=%s\n",
40+
record.Name, record.Content)
41+
if _, ok := known[record.Content]; ok {
42+
seen[record.Content] = true
43+
44+
if record.Proxied != cloudflareProxy || record.TTL != cloudflareTTL {
45+
log.Printf("updating dns record name=%s ip=%s\n",
46+
record.Name, record.Content)
47+
err := api.UpdateDNSRecord(zoneID, record.ID, cloudflare.DNSRecord{
48+
Type: record.Type,
49+
Name: record.Name,
50+
Content: record.Content,
51+
TTL: cloudflareTTL,
52+
Proxied: cloudflareProxy,
53+
})
54+
if err != nil {
55+
return errors.Wrapf(err, "failed to update dns record zone-id=%s record-id=%s name=%s ip=%s",
56+
zoneID, record.ID, record.Name, record.Content)
57+
}
58+
}
59+
} else {
60+
log.Printf("removing dns record name=%s ip=%s\n",
61+
record.Name, record.Content)
62+
err := api.DeleteDNSRecord(zoneID, record.ID)
63+
if err != nil {
64+
return errors.Wrapf(err, "failed to delete dns record zone-id=%s record-id=%s name=%s ip=%s",
65+
zoneID, record.ID, record.Name, record.Content)
66+
}
67+
}
68+
}
69+
70+
for ip := range known {
71+
if _, ok := seen[ip]; ok {
72+
continue
73+
}
74+
log.Printf("adding dns record name=%s ip=%s\n",
75+
dnsName, ip)
76+
_, err := api.CreateDNSRecord(zoneID, cloudflare.DNSRecord{
77+
Type: "A",
78+
Name: dnsName,
79+
Content: ip,
80+
TTL: cloudflareTTL,
81+
Proxied: cloudflareProxy,
82+
})
83+
if err != nil {
84+
return errors.Wrapf(err, "failed to create dns record zone-id=%s name=%s ip=%s",
85+
zoneID, dnsName, ip)
86+
}
87+
}
88+
}
89+
90+
return nil
91+
}
92+
93+
// findZoneID finds a zone id for the given dns record
94+
func findZoneID(api interface {
95+
ListZones(z ...string) ([]cloudflare.Zone, error)
96+
}, dnsName string) (string, error) {
97+
zones, err := api.ListZones()
98+
if err != nil {
99+
return "", err
100+
}
101+
102+
for _, zone := range zones {
103+
if zone.Name == dnsName || strings.HasSuffix(dnsName, "."+zone.Name) {
104+
return zone.ID, nil
105+
}
106+
}
107+
108+
return "", errors.New("zone id not found")
109+
}

sync_test.go

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package main
2+
3+
import (
4+
"testing"
5+
6+
cloudflare "github.com/cloudflare/cloudflare-go"
7+
"github.com/stretchr/testify/assert"
8+
)
9+
10+
type mockAPI struct {
11+
listZones func(z ...string) ([]cloudflare.Zone, error)
12+
}
13+
14+
func (m mockAPI) ListZones(z ...string) ([]cloudflare.Zone, error) {
15+
return m.listZones(z...)
16+
}
17+
18+
func TestFindZoneID(t *testing.T) {
19+
t.Run("subdomain", func(t *testing.T) {
20+
zoneID, err := findZoneID(mockAPI{
21+
listZones: func(z ...string) ([]cloudflare.Zone, error) {
22+
return []cloudflare.Zone{
23+
{ID: "1", Name: "example.com"},
24+
}, nil
25+
},
26+
}, "kubernetes.example.com")
27+
assert.Nil(t, err)
28+
assert.Equal(t, "1", zoneID)
29+
})
30+
t.Run("domain", func(t *testing.T) {
31+
zoneID, err := findZoneID(mockAPI{
32+
listZones: func(z ...string) ([]cloudflare.Zone, error) {
33+
return []cloudflare.Zone{
34+
{ID: "1", Name: "example.com"},
35+
}, nil
36+
},
37+
}, "example.com")
38+
assert.Nil(t, err)
39+
assert.Equal(t, "1", zoneID)
40+
})
41+
t.Run("partial domain", func(t *testing.T) {
42+
zoneID, err := findZoneID(mockAPI{
43+
listZones: func(z ...string) ([]cloudflare.Zone, error) {
44+
return []cloudflare.Zone{
45+
{ID: "1", Name: "example.com"}, // a bare suffix match would inadvertently match this domain
46+
{ID: "2", Name: "anotherexample.com"},
47+
}, nil
48+
},
49+
}, "anotherexample.com")
50+
assert.Nil(t, err)
51+
assert.Equal(t, "2", zoneID)
52+
})
53+
t.Run(".co.uk", func(t *testing.T) {
54+
zoneID, err := findZoneID(mockAPI{
55+
listZones: func(z ...string) ([]cloudflare.Zone, error) {
56+
return []cloudflare.Zone{
57+
{ID: "1", Name: "example.co.uk"},
58+
}, nil
59+
},
60+
}, "subdomain.example.co.uk")
61+
assert.Nil(t, err)
62+
assert.Equal(t, "1", zoneID)
63+
})
64+
}

0 commit comments

Comments
 (0)