From e43da949a3663d04f40d66633f29c85507cafaa7 Mon Sep 17 00:00:00 2001 From: Ross McFarland Date: Sat, 26 Aug 2017 15:39:54 -0700 Subject: [PATCH] Add CAA for CF, DNSimple, and README --- README.md | 6 ++-- octodns/provider/cloudflare.py | 24 ++++++++++++++- octodns/provider/dnsimple.py | 29 +++++++++++++++++-- .../cloudflare-dns_records-page-2.json | 23 +++++++++++++-- tests/fixtures/dnsimple-page-2.json | 18 +++++++++++- tests/test_octodns_provider_cloudflare.py | 12 ++++---- 6 files changed, 97 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 05cf979..1f103f1 100644 --- a/README.md +++ b/README.md @@ -150,12 +150,12 @@ The above command pulled the existing data out of Route53 and placed the results | Provider | Record Support | GeoDNS Support | Notes | |--|--|--|--| | [AzureProvider](/octodns/provider/azuredns.py) | A, AAAA, CNAME, MX, NS, PTR, SRV, TXT | No | | -| [CloudflareProvider](/octodns/provider/cloudflare.py) | A, AAAA, CNAME, MX, NS, SPF, TXT | No | | -| [DnsimpleProvider](/octodns/provider/dnsimple.py) | All | No | | +| [CloudflareProvider](/octodns/provider/cloudflare.py) | A, AAAA, CAA, CNAME, MX, NS, SPF, TXT | No | CAA tags restricted | +| [DnsimpleProvider](/octodns/provider/dnsimple.py) | All | No | CAA tags restricted | | [DynProvider](/octodns/provider/dyn.py) | All | Yes | | | [Ns1Provider](/octodns/provider/ns1.py) | All | No | | | [PowerDnsProvider](/octodns/provider/powerdns.py) | All | No | | -| [Route53](/octodns/provider/route53.py) | A, AAAA, CNAME, MX, NAPTR, NS, PTR, SPF, SRV, TXT | Yes | | +| [Route53](/octodns/provider/route53.py) | A, AAAA, CAA, CNAME, MX, NAPTR, NS, PTR, SPF, SRV, TXT | Yes | | | [TinyDNSSource](/octodns/source/tinydns.py) | A, CNAME, MX, NS, PTR | No | read-only | | [YamlProvider](/octodns/provider/yaml.py) | All | Yes | config | diff --git a/octodns/provider/cloudflare.py b/octodns/provider/cloudflare.py index 2ee8f8b..a4fce9b 100644 --- a/octodns/provider/cloudflare.py +++ b/octodns/provider/cloudflare.py @@ -36,7 +36,7 @@ class CloudflareProvider(BaseProvider): ''' SUPPORTS_GEO = False # TODO: support SRV - SUPPORTS = set(('A', 'AAAA', 'CNAME', 'MX', 'NS', 'SPF', 'TXT')) + SUPPORTS = set(('A', 'AAAA', 'CAA', 'CNAME', 'MX', 'NS', 'SPF', 'TXT')) MIN_TTL = 120 TIMEOUT = 15 @@ -104,6 +104,20 @@ class CloudflareProvider(BaseProvider): 'values': [r['content'].replace(';', '\;') for r in records], } + def _data_for_CAA(self, _type, records): + values = [] + for r in records: + values.append({ + 'flags': r['flags'], + 'tag': r['tag'], + 'value': r['content'], + }) + return { + 'ttl': records[0]['ttl'], + 'type': _type, + 'values': values, + } + def _data_for_CNAME(self, _type, records): only = records[0] return { @@ -197,6 +211,14 @@ class CloudflareProvider(BaseProvider): _contents_for_NS = _contents_for_multiple _contents_for_SPF = _contents_for_multiple + def _contents_for_CAA(self, record): + for value in record.values: + yield { + 'flags': value.flags, + 'tag': value.tag, + 'value': value.value, + } + def _contents_for_TXT(self, record): for value in record.values: yield {'content': value.replace('\;', ';')} diff --git a/octodns/provider/dnsimple.py b/octodns/provider/dnsimple.py index dc44d1b..43b5b9b 100644 --- a/octodns/provider/dnsimple.py +++ b/octodns/provider/dnsimple.py @@ -91,8 +91,8 @@ class DnsimpleProvider(BaseProvider): account: 42 ''' SUPPORTS_GEO = False - SUPPORTS = set(('A', 'AAAA', 'ALIAS', 'CNAME', 'MX', 'NAPTR', 'NS', 'PTR', - 'SPF', 'SRV', 'SSHFP', 'TXT')) + SUPPORTS = set(('A', 'AAAA', 'ALIAS', 'CAA', 'CNAME', 'MX', 'NAPTR', 'NS', + 'PTR', 'SPF', 'SRV', 'SSHFP', 'TXT')) def __init__(self, id, token, account, *args, **kwargs): self.log = logging.getLogger('DnsimpleProvider[{}]'.format(id)) @@ -114,6 +114,21 @@ class DnsimpleProvider(BaseProvider): _data_for_SPF = _data_for_multiple _data_for_TXT = _data_for_multiple + def _data_for_CAA(self, _type, records): + values = [] + for record in records: + flags, tag, value = record['content'].split(' ') + values.append({ + 'flags': flags, + 'tag': tag, + 'value': value[1:-1], + }) + return { + 'ttl': records[0]['ttl'], + 'type': _type, + 'values': values + } + def _data_for_CNAME(self, _type, records): record = records[0] return { @@ -275,6 +290,16 @@ class DnsimpleProvider(BaseProvider): _params_for_SPF = _params_for_multiple _params_for_TXT = _params_for_multiple + def _params_for_CAA(self, record): + for value in record.values: + yield { + 'content': '{} {} "{}"'.format(value.flags, value.tag, + value.value), + 'name': record.name, + 'ttl': record.ttl, + 'type': record._type + } + def _params_for_single(self, record): yield { 'content': record.value, diff --git a/tests/fixtures/cloudflare-dns_records-page-2.json b/tests/fixtures/cloudflare-dns_records-page-2.json index 24c49d5..9800155 100644 --- a/tests/fixtures/cloudflare-dns_records-page-2.json +++ b/tests/fixtures/cloudflare-dns_records-page-2.json @@ -118,14 +118,33 @@ "meta": { "auto_added": false } + }, + { + "id": "fc223b34cd5611334422ab3322997667", + "type": "CAA", + "name": "unit.tests", + "content": "ca.unit.tests", + "flags": 0, + "tag": "issue", + "proxiable": false, + "proxied": false, + "ttl": 3600, + "locked": false, + "zone_id": "ff12ab34cd5611334422ab3322997650", + "zone_name": "unit.tests", + "modified_on": "2017-03-11T18:01:42.961566Z", + "created_on": "2017-03-11T18:01:42.961566Z", + "meta": { + "auto_added": false + } } ], "result_info": { "page": 2, "per_page": 10, "total_pages": 2, - "count": 7, - "total_count": 17 + "count": 8, + "total_count": 19 }, "success": true, "errors": [], diff --git a/tests/fixtures/dnsimple-page-2.json b/tests/fixtures/dnsimple-page-2.json index f50704b..40aaa48 100644 --- a/tests/fixtures/dnsimple-page-2.json +++ b/tests/fixtures/dnsimple-page-2.json @@ -159,12 +159,28 @@ "system_record": false, "created_at": "2017-03-09T15:55:09Z", "updated_at": "2017-03-09T15:55:09Z" + }, + { + "id": 12188803, + "zone_id": "unit.tests", + "parent_id": null, + "name": "", + "content": "0 issue \"ca.unit.tests\"", + "ttl": 3600, + "priority": null, + "type": "CAA", + "regions": [ + "global" + ], + "system_record": false, + "created_at": "2017-03-09T15:55:09Z", + "updated_at": "2017-03-09T15:55:09Z" } ], "pagination": { "current_page": 2, "per_page": 20, - "total_entries": 29, + "total_entries": 30, "total_pages": 2 } } diff --git a/tests/test_octodns_provider_cloudflare.py b/tests/test_octodns_provider_cloudflare.py index 5dcae30..04a46e0 100644 --- a/tests/test_octodns_provider_cloudflare.py +++ b/tests/test_octodns_provider_cloudflare.py @@ -118,7 +118,7 @@ class TestCloudflareProvider(TestCase): zone = Zone('unit.tests.', []) provider.populate(zone) - self.assertEquals(9, len(zone.records)) + self.assertEquals(10, len(zone.records)) changes = self.expected.changes(zone, provider) self.assertEquals(0, len(changes)) @@ -126,7 +126,7 @@ class TestCloudflareProvider(TestCase): # re-populating the same zone/records comes out of cache, no calls again = Zone('unit.tests.', []) provider.populate(again) - self.assertEquals(9, len(again.records)) + self.assertEquals(10, len(again.records)) def test_apply(self): provider = CloudflareProvider('test', 'email', 'token') @@ -140,12 +140,12 @@ class TestCloudflareProvider(TestCase): 'id': 42, } }, # zone create - ] + [None] * 16 # individual record creates + ] + [None] * 17 # individual record creates # non-existant zone, create everything plan = provider.plan(self.expected) - self.assertEquals(9, len(plan.changes)) - self.assertEquals(9, provider.apply(plan)) + self.assertEquals(10, len(plan.changes)) + self.assertEquals(10, provider.apply(plan)) provider._request.assert_has_calls([ # created the domain @@ -170,7 +170,7 @@ class TestCloudflareProvider(TestCase): }), ], True) # expected number of total calls - self.assertEquals(18, provider._request.call_count) + self.assertEquals(19, provider._request.call_count) provider._request.reset_mock()