Skip to content

Commit

Permalink
first working version
Browse files Browse the repository at this point in the history
  • Loading branch information
Chris Byron committed May 20, 2016
1 parent 812a46c commit 5d6d03c
Show file tree
Hide file tree
Showing 7 changed files with 505 additions and 119 deletions.
64 changes: 44 additions & 20 deletions dns/cloudflare/cloudflare.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ import (
// cf.API implements this interface
type CloudflareAPI interface {
CreateDNSRecord(string, cf.DNSRecord) (*cf.DNSRecordResponse, error)
UpdateDNSRecord(string, string, cf.DNSRecord) error
DeleteDNSRecord(string, string) error
DNSRecords(string, cf.DNSRecord) ([]cf.DNSRecord, error)
DNSRecord(string, string) (cf.DNSRecord, error)
}

// implements CloudflareAPI while wrapping the actual CF API object
Expand Down Expand Up @@ -43,12 +45,12 @@ type CloudflareAPI interface {
//}

// Implements dns.DNSAPI
type CloudflareUpdater struct {
type CloudflareAPIClient struct {
ZoneID string
Api CloudflareAPI
}

func NewCloudflareUpdater(zoneName string) *CloudflareUpdater {
func NewCloudflareAPIClient(zoneName string) *CloudflareAPIClient {
api, newErr := cf.New(os.Getenv("CF_API_KEY"), os.Getenv("CF_API_EMAIL"))
if newErr != nil {
panic(newErr)
Expand All @@ -60,19 +62,46 @@ func NewCloudflareUpdater(zoneName string) *CloudflareUpdater {
if len(zones) != 1 {
panic("didn't find exactly one zone named " + zoneName)
}
return &CloudflareUpdater{
return &CloudflareAPIClient{
ZoneID: zones[0].ID,
Api: api,
}
}

func (cfu *CloudflareUpdater) WriteTXTRecord(name, txt string) (string, error) {
// Find a set of IDs that match the text filter
func (c *CloudflareAPIClient) FilterTXTRecords(name, filter string) ([]string, error) {
rr := cf.DNSRecord{
Type: "TXT",
Name: name,
}
records, err := c.Api.DNSRecords(c.ZoneID, rr)
if err != nil {
return []string{}, err
}
results := []string{}
for _, record := range records {
if strings.Contains(record.Content, filter) {
results = append(results, record.ID)
}
}
return results, nil
}

func (c *CloudflareAPIClient) GetTXTRecordContent(id string) (string, error) {
if record, err := c.Api.DNSRecord(c.ZoneID, id); err != nil {
return "", err
} else {
return record.Content, nil
}
}

func (c *CloudflareAPIClient) WriteTXTRecord(name, txt string) (string, error) {
rr := cf.DNSRecord{
Type: "TXT",
Name: name,
Content: txt,
}
response, err := cfu.Api.CreateDNSRecord(cfu.ZoneID, rr)
response, err := c.Api.CreateDNSRecord(c.ZoneID, rr)
if err != nil {
return "", err
}
Expand All @@ -83,25 +112,20 @@ func (cfu *CloudflareUpdater) WriteTXTRecord(name, txt string) (string, error) {
return id, err
}

func (cfu *CloudflareUpdater) DeleteTXTRecordByName(name string) error {
// Update does not change the ID
func (c *CloudflareAPIClient) UpdateTXTRecord(id, name, txt string) (string, error) {
rr := cf.DNSRecord{
Type: "TXT",
Name: name,
Type: "TXT",
Name: name,
Content: txt,
}
records, err := cfu.Api.DNSRecords(cfu.ZoneID, rr)
err := c.Api.UpdateDNSRecord(c.ZoneID, id, rr)
if err != nil {
return err
}
if len(records) != 1 {
return errors.New("didn't find exactly one txt record named " + name)
}
if records[0].Type != "TXT" {
return errors.New("Cannot delete DNS record because type is not TXT: " + records[0].Type)
return "", err
}
id := records[0].ID
return cfu.DeleteTXTRecord(id)
return id, err
}

func (cfu *CloudflareUpdater) DeleteTXTRecord(id string) error {
return cfu.Api.DeleteDNSRecord(cfu.ZoneID, id)
func (c *CloudflareAPIClient) DeleteTXTRecord(id string) error {
return c.Api.DeleteDNSRecord(c.ZoneID, id)
}
111 changes: 96 additions & 15 deletions dns/cloudflare/cloudflare_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package cloudflare
import (
cf "github.com/cloudflare/cloudflare-go"
"github.com/golang/mock/gomock"
mock_dns "github.com/lordbyron/auto-spf-flattener/dns/cloudflare/mock_cloudflare"
mock_cloudflare "github.com/lordbyron/auto-spf-flattener/dns/cloudflare/mock_cloudflare"
"testing"
)

Expand All @@ -13,6 +13,72 @@ const TestDomain = "_spf.example.com"
const TestSPFTXT = "v=spf1 ip4:1.2.3.4/5 ~all"
const TestRecordID = "TXT4321"

func TestFilterTXTRecords(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

expectedRr := cf.DNSRecord{
Type: "TXT",
Name: TestDomain,
}
response := []cf.DNSRecord{
cf.DNSRecord{
ID: TestRecordID,
Content: TestSPFTXT,
},
cf.DNSRecord{
ID: "bad_id",
Content: "nothing to see here",
},
}

mockCloudflare := mock_cloudflare.NewMockCloudflareAPI(ctrl)
mockCloudflare.EXPECT().DNSRecords(TestZoneID, expectedRr).Return(response, nil)

client := &CloudflareAPIClient{
ZoneID: TestZoneID,
Api: mockCloudflare,
}

ids, err := client.FilterTXTRecords(TestDomain, "spf1")
if err != nil {
t.Errorf("Error writing TXT record: %s", err)
}
if len(ids) != 1 {
t.Errorf("Wrong number of records returned: %d", len(ids))
}
if ids[0] != TestRecordID {
t.Errorf("Wrong record ID returned: %s", ids[0])
}
}

func TestGetTXTRecordContent(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

response := cf.DNSRecord{
Type: "TXT",
Name: TestDomain,
Content: TestSPFTXT,
}

mockCloudflare := mock_cloudflare.NewMockCloudflareAPI(ctrl)
mockCloudflare.EXPECT().DNSRecord(TestZoneID, TestRecordID).Return(response, nil)

client := &CloudflareAPIClient{
ZoneID: TestZoneID,
Api: mockCloudflare,
}

content, err := client.GetTXTRecordContent(TestRecordID)
if err != nil {
t.Errorf("Error getting TXT record: %s", err)
}
if content != TestSPFTXT {
t.Errorf("Wrong content returned during fetch: `%s`", content)
}
}

func TestWriteTXT(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
Expand All @@ -31,15 +97,15 @@ func TestWriteTXT(t *testing.T) {
},
}

mockCloudflare := mock_dns.NewMockCloudflareAPI(ctrl)
mockCloudflare := mock_cloudflare.NewMockCloudflareAPI(ctrl)
mockCloudflare.EXPECT().CreateDNSRecord(TestZoneID, expectedRr).Return(response, nil)

cfu := &CloudflareUpdater{
client := &CloudflareAPIClient{
ZoneID: TestZoneID,
Api: mockCloudflare,
}

id, err := cfu.WriteTXTRecord(TestDomain, TestSPFTXT)
id, err := client.WriteTXTRecord(TestDomain, TestSPFTXT)
if err != nil {
t.Errorf("Error writing TXT record: %s", err)
}
Expand All @@ -48,31 +114,46 @@ func TestWriteTXT(t *testing.T) {
}
}

func TestDeleteTXTRecordByName(t *testing.T) {
func TestUpdateTXTRecord(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

expectedRr := cf.DNSRecord{
Type: "TXT",
Name: TestDomain,
}
response := []cf.DNSRecord{cf.DNSRecord{
ID: TestRecordID,
Type: "TXT",
Name: TestDomain,
Content: TestSPFTXT,
}}
}

mockCloudflare := mock_dns.NewMockCloudflareAPI(ctrl)
mockCloudflare.EXPECT().DNSRecords(TestZoneID, expectedRr).Return(response, nil)
mockCloudflare := mock_cloudflare.NewMockCloudflareAPI(ctrl)
mockCloudflare.EXPECT().UpdateDNSRecord(TestZoneID, TestRecordID, expectedRr).Return(nil)

client := &CloudflareAPIClient{
ZoneID: TestZoneID,
Api: mockCloudflare,
}

id, err := client.UpdateTXTRecord(TestRecordID, TestDomain, TestSPFTXT)
if err != nil {
t.Errorf("Error deleting TXT record: %s", err)
}
if id != TestRecordID {
t.Errorf("Wrong id returned: %s", id)
}
}

func TestDeleteTXTRecord(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

mockCloudflare := mock_cloudflare.NewMockCloudflareAPI(ctrl)
mockCloudflare.EXPECT().DeleteDNSRecord(TestZoneID, TestRecordID).Return(nil)

cfu := &CloudflareUpdater{
client := &CloudflareAPIClient{
ZoneID: TestZoneID,
Api: mockCloudflare,
}

err := cfu.DeleteTXTRecordByName(TestDomain)
err := client.DeleteTXTRecord(TestRecordID)
if err != nil {
t.Errorf("Error deleting TXT record: %s", err)
}
Expand Down
21 changes: 21 additions & 0 deletions dns/cloudflare/mock_cloudflare/mock_cloudflare.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@ func (_mr *_MockCloudflareAPIRecorder) CreateDNSRecord(arg0, arg1 interface{}) *
return _mr.mock.ctrl.RecordCall(_mr.mock, "CreateDNSRecord", arg0, arg1)
}

func (_m *MockCloudflareAPI) UpdateDNSRecord(_param0 string, _param1 string, _param2 cloudflare_go.DNSRecord) error {
ret := _m.ctrl.Call(_m, "UpdateDNSRecord", _param0, _param1, _param2)
ret0, _ := ret[0].(error)
return ret0
}

func (_mr *_MockCloudflareAPIRecorder) UpdateDNSRecord(arg0, arg1, arg2 interface{}) *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "UpdateDNSRecord", arg0, arg1, arg2)
}

func (_m *MockCloudflareAPI) DeleteDNSRecord(_param0 string, _param1 string) error {
ret := _m.ctrl.Call(_m, "DeleteDNSRecord", _param0, _param1)
ret0, _ := ret[0].(error)
Expand All @@ -60,3 +70,14 @@ func (_m *MockCloudflareAPI) DNSRecords(_param0 string, _param1 cloudflare_go.DN
func (_mr *_MockCloudflareAPIRecorder) DNSRecords(arg0, arg1 interface{}) *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "DNSRecords", arg0, arg1)
}

func (_m *MockCloudflareAPI) DNSRecord(_param0 string, _param1 string) (cloudflare_go.DNSRecord, error) {
ret := _m.ctrl.Call(_m, "DNSRecord", _param0, _param1)
ret0, _ := ret[0].(cloudflare_go.DNSRecord)
ret1, _ := ret[1].(error)
return ret0, ret1
}

func (_mr *_MockCloudflareAPIRecorder) DNSRecord(arg0, arg1 interface{}) *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "DNSRecord", arg0, arg1)
}
Loading

0 comments on commit 5d6d03c

Please sign in to comment.