From 9ff5942d1934a693b0e8bc4f5d71ef2a48659ed8 Mon Sep 17 00:00:00 2001 From: Matt Date: Sat, 1 Sep 2018 13:31:53 +1000 Subject: [PATCH 1/5] Add: Ability to manage "proxied" flag of "A", "AAAA" and "CNAME" records via YAML configuration (see "CloudflareProvider" class docstring for details). --- octodns/provider/cloudflare.py | 57 ++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/octodns/provider/cloudflare.py b/octodns/provider/cloudflare.py index 50e4b90..a418169 100644 --- a/octodns/provider/cloudflare.py +++ b/octodns/provider/cloudflare.py @@ -6,6 +6,7 @@ from __future__ import absolute_import, division, print_function, \ unicode_literals from collections import defaultdict +from copy import deepcopy from logging import getLogger from requests import Session @@ -27,6 +28,9 @@ class CloudflareAuthenticationError(CloudflareError): CloudflareError.__init__(self, data) +_PROXIABLE_RECORD_TYPES = {'A', 'AAAA', 'CNAME'} + + class CloudflareProvider(BaseProvider): ''' Cloudflare DNS provider @@ -221,7 +225,14 @@ class CloudflareProvider(BaseProvider): data_for = getattr(self, '_data_for_{}'.format(_type)) data = data_for(_type, records) - return Record.new(zone, name, data, source=self, lenient=lenient) + record = Record.new(zone, name, data, source=self, lenient=lenient) + + if _type in _PROXIABLE_RECORD_TYPES: + record._octodns['cloudflare'] = { + 'proxied': records[0].get('proxied', False) + } + + return record def populate(self, zone, target=False, lenient=False): self.log.debug('populate: name=%s, target=%s, lenient=%s', zone.name, @@ -260,8 +271,18 @@ class CloudflareProvider(BaseProvider): def _include_change(self, change): if isinstance(change, Update): - existing = change.existing.data new = change.new.data + + # Cloudflare manages TTL of proxied records, so we should exclude + # TTL from the comparison (to prevent false-positives). + if self._record_is_proxied(change.existing): + existing = deepcopy(change.existing.data) + existing.update({ + 'ttl': new['ttl'] + }) + else: + existing = change.existing.data + new['ttl'] = max(self.MIN_TTL, new['ttl']) if new == existing: return False @@ -322,6 +343,12 @@ class CloudflareProvider(BaseProvider): } } + def _record_is_proxied(self, record): + return ( + not self.cdn and + record._octodns.get('cloudflare', {}).get('proxied', False) + ) + def _gen_data(self, record): name = record.fqdn[:-1] _type = record._type @@ -338,6 +365,12 @@ class CloudflareProvider(BaseProvider): 'type': _type, 'ttl': ttl, }) + + if _type in _PROXIABLE_RECORD_TYPES: + content.update({ + 'proxied': self._record_is_proxied(record) + }) + yield content def _gen_key(self, data): @@ -512,3 +545,23 @@ class CloudflareProvider(BaseProvider): # clear the cache self._zone_records.pop(name, None) + + def _extra_changes(self, existing, desired, changes): + extra_changes = [] + + existing_records = {r: r for r in existing.records} + changed_records = {c.record for c in changes} + + for desired_record in desired.records: + if desired_record not in existing.records: # Will be created + continue + elif desired_record in changed_records: # Already being updated + continue + + existing_record = existing_records[desired_record] + + if (self._record_is_proxied(existing_record) != + self._record_is_proxied(desired_record)): + extra_changes.append(Update(existing_record, desired_record)) + + return extra_changes From 18f29f1c6b791cab2e90d4e05b17c3b2ca164711 Mon Sep 17 00:00:00 2001 From: Matt Date: Sun, 2 Sep 2018 18:02:27 +1000 Subject: [PATCH 2/5] Alter: Existing tests. --- tests/test_octodns_provider_cloudflare.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test_octodns_provider_cloudflare.py b/tests/test_octodns_provider_cloudflare.py index 0f769a4..d707d62 100644 --- a/tests/test_octodns_provider_cloudflare.py +++ b/tests/test_octodns_provider_cloudflare.py @@ -294,6 +294,7 @@ class TestCloudflareProvider(TestCase): 'content': '3.2.3.4', 'type': 'A', 'name': 'ttl.unit.tests', + 'proxied': False, 'ttl': 300 }), call('DELETE', '/zones/ff12ab34cd5611334422ab3322997650/' @@ -386,6 +387,7 @@ class TestCloudflareProvider(TestCase): 'content': '4.4.4.4', 'type': 'A', 'name': 'a.unit.tests', + 'proxied': False, 'ttl': 300 }), call('PUT', '/zones/42/dns_records/' @@ -393,6 +395,7 @@ class TestCloudflareProvider(TestCase): 'content': '2.2.2.2', 'type': 'A', 'name': 'a.unit.tests', + 'proxied': False, 'ttl': 300 }), call('PUT', '/zones/42/dns_records/' @@ -400,6 +403,7 @@ class TestCloudflareProvider(TestCase): 'content': '3.3.3.3', 'type': 'A', 'name': 'a.unit.tests', + 'proxied': False, 'ttl': 300 }), ]) @@ -532,6 +536,7 @@ class TestCloudflareProvider(TestCase): self.assertEquals({ 'content': 'www.unit.tests.', 'name': 'unit.tests', + 'proxied': False, 'ttl': 300, 'type': 'CNAME' }, list(contents)[0]) From 6ceb35c2fcbeab8086056f1535b132e802e6b117 Mon Sep 17 00:00:00 2001 From: Matt Date: Thu, 6 Sep 2018 17:37:55 +1000 Subject: [PATCH 3/5] Add: New tests. --- tests/test_octodns_provider_cloudflare.py | 471 ++++++++++++++++++++++ 1 file changed, 471 insertions(+) diff --git a/tests/test_octodns_provider_cloudflare.py b/tests/test_octodns_provider_cloudflare.py index d707d62..544d0fb 100644 --- a/tests/test_octodns_provider_cloudflare.py +++ b/tests/test_octodns_provider_cloudflare.py @@ -18,6 +18,17 @@ from octodns.provider.yaml import YamlProvider from octodns.zone import Zone +def set_record_proxied_flag(record, proxied): + try: + record._octodns['cloudflare']['proxied'] = proxied + except KeyError: + record._octodns['cloudflare'] = { + 'proxied': proxied + } + + return record + + class TestCloudflareProvider(TestCase): expected = Zone('unit.tests.', []) source = YamlProvider('test', join(dirname(__file__), 'config')) @@ -757,3 +768,463 @@ class TestCloudflareProvider(TestCase): plan = provider.plan(wanted) self.assertEquals(False, hasattr(plan, 'changes')) + + def test_proxied_ignore_ttl(self): + provider = CloudflareProvider('test', 'email', 'token') + + provider.zone_records = Mock(return_value=[ + { + "id": "fc12ab34cd5611334422ab3322997642", + "type": "A", + "name": "unit.tests.a", + "content": "1.2.3.4", + "proxiable": True, + "proxied": True, + "ttl": 1, + "locked": False, + "zone_id": "ff12ab34cd5611334422ab3322997650", + "zone_name": "unit.tests", + "modified_on": "2017-03-11T18:01:43.420689Z", + "created_on": "2017-03-11T18:01:43.420689Z", + "meta": { + "auto_added": False + } + }, + { + "id": "fc12ab34cd5611334422ab3322997643", + "type": "AAAA", + "name": "unit.tests.aaaa", + "content": "::1", + "proxiable": True, + "proxied": True, + "ttl": 1, + "locked": False, + "zone_id": "ff12ab34cd5611334422ab3322997650", + "zone_name": "unit.tests", + "modified_on": "2017-03-11T18:01:43.420689Z", + "created_on": "2017-03-11T18:01:43.420689Z", + "meta": { + "auto_added": False + } + }, + { + "id": "fc12ab34cd5611334422ab3322997644", + "type": "CNAME", + "name": "unit.tests.cname", + "content": "www.unit.tests", + "proxiable": True, + "proxied": True, + "ttl": 1, + "locked": False, + "zone_id": "ff12ab34cd5611334422ab3322997650", + "zone_name": "unit.tests", + "modified_on": "2017-03-11T18:01:43.420689Z", + "created_on": "2017-03-11T18:01:43.420689Z", + "meta": { + "auto_added": False + } + }, + ]) + + zone = Zone('unit.tests.', []) + provider.populate(zone) + + wanted = Zone('unit.tests.', []) + wanted.add_record( + set_record_proxied_flag( + Record.new(wanted, 'unit.tests.a', { + 'ttl': 120, + 'type': 'A', + 'value': '1.2.3.4' + }), True + ) + ) + wanted.add_record( + set_record_proxied_flag( + Record.new(wanted, 'unit.tests.aaaa', { + 'ttl': 250, + 'type': 'AAAA', + 'value': '::1' + }), True + ) + ) + wanted.add_record( + set_record_proxied_flag( + Record.new(wanted, 'unit.tests.cname', { + 'ttl': 300, + 'type': 'CNAME', + 'value': 'www.unit.tests.' + }), True + ) + ) + + plan = provider.plan(wanted) + self.assertEquals(False, hasattr(plan, 'changes')) + + def test_enable_proxied(self): + provider = CloudflareProvider('test', 'email', 'token') + + provider.zone_records = Mock(return_value=[ + { + "id": "fc12ab34cd5611334422ab3322997642", + "type": "A", + "name": "unit.tests.a", + "content": "1.2.3.4", + "proxiable": True, + "proxied": False, + "ttl": 120, + "locked": False, + "zone_id": "ff12ab34cd5611334422ab3322997650", + "zone_name": "unit.tests", + "modified_on": "2017-03-11T18:01:43.420689Z", + "created_on": "2017-03-11T18:01:43.420689Z", + "meta": { + "auto_added": False + } + }, + { + "id": "fc12ab34cd5611334422ab3322997643", + "type": "AAAA", + "name": "unit.tests.aaaa", + "content": "::1", + "proxiable": True, + "proxied": False, + "ttl": 250, + "locked": False, + "zone_id": "ff12ab34cd5611334422ab3322997650", + "zone_name": "unit.tests", + "modified_on": "2017-03-11T18:01:43.420689Z", + "created_on": "2017-03-11T18:01:43.420689Z", + "meta": { + "auto_added": False + } + }, + { + "id": "fc12ab34cd5611334422ab3322997644", + "type": "CNAME", + "name": "unit.tests.cname", + "content": "www.unit.tests", + "proxiable": True, + "proxied": False, + "ttl": 300, + "locked": False, + "zone_id": "ff12ab34cd5611334422ab3322997650", + "zone_name": "unit.tests", + "modified_on": "2017-03-11T18:01:43.420689Z", + "created_on": "2017-03-11T18:01:43.420689Z", + "meta": { + "auto_added": False + } + }, + ]) + + zone = Zone('unit.tests.', []) + provider.populate(zone) + + wanted = Zone('unit.tests.', []) + wanted.add_record( + set_record_proxied_flag( + Record.new(wanted, 'unit.tests.a', { + 'ttl': 120, + 'type': 'A', + 'value': '1.2.3.4' + }), True + ) + ) + wanted.add_record( + set_record_proxied_flag( + Record.new(wanted, 'unit.tests.aaaa', { + 'ttl': 250, + 'type': 'AAAA', + 'value': '::1' + }), True + ) + ) + wanted.add_record( + set_record_proxied_flag( + Record.new(wanted, 'unit.tests.cname', { + 'ttl': 300, + 'type': 'CNAME', + 'value': 'www.unit.tests.' + }), True + ) + ) + + plan = provider.plan(wanted) + self.assertEquals(True, hasattr(plan, 'changes')) + + def test_disable_proxied(self): + provider = CloudflareProvider('test', 'email', 'token') + + provider.zone_records = Mock(return_value=[ + { + "id": "fc12ab34cd5611334422ab3322997642", + "type": "A", + "name": "unit.tests.a", + "content": "1.2.3.4", + "proxiable": True, + "proxied": True, + "ttl": 120, + "locked": False, + "zone_id": "ff12ab34cd5611334422ab3322997650", + "zone_name": "unit.tests", + "modified_on": "2017-03-11T18:01:43.420689Z", + "created_on": "2017-03-11T18:01:43.420689Z", + "meta": { + "auto_added": False + } + }, + { + "id": "fc12ab34cd5611334422ab3322997643", + "type": "AAAA", + "name": "unit.tests.aaaa", + "content": "::1", + "proxiable": True, + "proxied": True, + "ttl": 250, + "locked": False, + "zone_id": "ff12ab34cd5611334422ab3322997650", + "zone_name": "unit.tests", + "modified_on": "2017-03-11T18:01:43.420689Z", + "created_on": "2017-03-11T18:01:43.420689Z", + "meta": { + "auto_added": False + } + }, + { + "id": "fc12ab34cd5611334422ab3322997644", + "type": "CNAME", + "name": "unit.tests.cname", + "content": "www.unit.tests", + "proxiable": True, + "proxied": True, + "ttl": 300, + "locked": False, + "zone_id": "ff12ab34cd5611334422ab3322997650", + "zone_name": "unit.tests", + "modified_on": "2017-03-11T18:01:43.420689Z", + "created_on": "2017-03-11T18:01:43.420689Z", + "meta": { + "auto_added": False + } + }, + ]) + + zone = Zone('unit.tests.', []) + provider.populate(zone) + + wanted = Zone('unit.tests.', []) + wanted.add_record( + set_record_proxied_flag( + Record.new(wanted, 'unit.tests.a', { + 'ttl': 120, + 'type': 'A', + 'value': '1.2.3.4' + }), False + ) + ) + wanted.add_record( + set_record_proxied_flag( + Record.new(wanted, 'unit.tests.aaaa', { + 'ttl': 250, + 'type': 'AAAA', + 'value': '::1' + }), False + ) + ) + wanted.add_record( + set_record_proxied_flag( + Record.new(wanted, 'unit.tests.cname', { + 'ttl': 300, + 'type': 'CNAME', + 'value': 'www.unit.tests.' + }), False + ) + ) + + plan = provider.plan(wanted) + self.assertEquals(True, hasattr(plan, 'changes')) + + def test_leave_proxied_disabled(self): + provider = CloudflareProvider('test', 'email', 'token') + + provider.zone_records = Mock(return_value=[ + { + "id": "fc12ab34cd5611334422ab3322997642", + "type": "A", + "name": "unit.tests.a", + "content": "1.2.3.4", + "proxiable": True, + "proxied": False, + "ttl": 120, + "locked": False, + "zone_id": "ff12ab34cd5611334422ab3322997650", + "zone_name": "unit.tests", + "modified_on": "2017-03-11T18:01:43.420689Z", + "created_on": "2017-03-11T18:01:43.420689Z", + "meta": { + "auto_added": False + } + }, + { + "id": "fc12ab34cd5611334422ab3322997643", + "type": "AAAA", + "name": "unit.tests.aaaa", + "content": "::1", + "proxiable": True, + "proxied": False, + "ttl": 250, + "locked": False, + "zone_id": "ff12ab34cd5611334422ab3322997650", + "zone_name": "unit.tests", + "modified_on": "2017-03-11T18:01:43.420689Z", + "created_on": "2017-03-11T18:01:43.420689Z", + "meta": { + "auto_added": False + } + }, + { + "id": "fc12ab34cd5611334422ab3322997644", + "type": "CNAME", + "name": "unit.tests.cname", + "content": "www.unit.tests", + "proxiable": True, + "proxied": False, + "ttl": 300, + "locked": False, + "zone_id": "ff12ab34cd5611334422ab3322997650", + "zone_name": "unit.tests", + "modified_on": "2017-03-11T18:01:43.420689Z", + "created_on": "2017-03-11T18:01:43.420689Z", + "meta": { + "auto_added": False + } + }, + ]) + + zone = Zone('unit.tests.', []) + provider.populate(zone) + + wanted = Zone('unit.tests.', []) + wanted.add_record( + set_record_proxied_flag( + Record.new(wanted, 'unit.tests.a', { + 'ttl': 120, + 'type': 'A', + 'value': '1.2.3.4' + }), False + ) + ) + wanted.add_record( + set_record_proxied_flag( + Record.new(wanted, 'unit.tests.aaaa', { + 'ttl': 250, + 'type': 'AAAA', + 'value': '::1' + }), False + ) + ) + wanted.add_record( + set_record_proxied_flag( + Record.new(wanted, 'unit.tests.cname', { + 'ttl': 300, + 'type': 'CNAME', + 'value': 'www.unit.tests.' + }), False + ) + ) + + plan = provider.plan(wanted) + self.assertEquals(False, hasattr(plan, 'changes')) + + def test_leave_proxied_enabled(self): + provider = CloudflareProvider('test', 'email', 'token') + + provider.zone_records = Mock(return_value=[ + { + "id": "fc12ab34cd5611334422ab3322997642", + "type": "A", + "name": "unit.tests.a", + "content": "1.2.3.4", + "proxiable": True, + "proxied": True, + "ttl": 120, + "locked": False, + "zone_id": "ff12ab34cd5611334422ab3322997650", + "zone_name": "unit.tests", + "modified_on": "2017-03-11T18:01:43.420689Z", + "created_on": "2017-03-11T18:01:43.420689Z", + "meta": { + "auto_added": False + } + }, + { + "id": "fc12ab34cd5611334422ab3322997643", + "type": "AAAA", + "name": "unit.tests.aaaa", + "content": "::1", + "proxiable": True, + "proxied": True, + "ttl": 250, + "locked": False, + "zone_id": "ff12ab34cd5611334422ab3322997650", + "zone_name": "unit.tests", + "modified_on": "2017-03-11T18:01:43.420689Z", + "created_on": "2017-03-11T18:01:43.420689Z", + "meta": { + "auto_added": False + } + }, + { + "id": "fc12ab34cd5611334422ab3322997644", + "type": "CNAME", + "name": "unit.tests.cname", + "content": "www.unit.tests", + "proxiable": True, + "proxied": True, + "ttl": 300, + "locked": False, + "zone_id": "ff12ab34cd5611334422ab3322997650", + "zone_name": "unit.tests", + "modified_on": "2017-03-11T18:01:43.420689Z", + "created_on": "2017-03-11T18:01:43.420689Z", + "meta": { + "auto_added": False + } + }, + ]) + + zone = Zone('unit.tests.', []) + provider.populate(zone) + + wanted = Zone('unit.tests.', []) + wanted.add_record( + set_record_proxied_flag( + Record.new(wanted, 'unit.tests.a', { + 'ttl': 120, + 'type': 'A', + 'value': '1.2.3.4' + }), True + ) + ) + wanted.add_record( + set_record_proxied_flag( + Record.new(wanted, 'unit.tests.aaaa', { + 'ttl': 250, + 'type': 'AAAA', + 'value': '::1' + }), True + ) + ) + wanted.add_record( + set_record_proxied_flag( + Record.new(wanted, 'unit.tests.cname', { + 'ttl': 300, + 'type': 'CNAME', + 'value': 'www.unit.tests.' + }), True + ) + ) + + plan = provider.plan(wanted) + self.assertEquals(False, hasattr(plan, 'changes')) From a0eaefb3304e86489c33beef4c0b454db2757266 Mon Sep 17 00:00:00 2001 From: Matt Date: Thu, 6 Sep 2018 18:02:31 +1000 Subject: [PATCH 4/5] Add: Documentation on how to utilise the new behaviour. --- octodns/provider/cloudflare.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/octodns/provider/cloudflare.py b/octodns/provider/cloudflare.py index a418169..255f68b 100644 --- a/octodns/provider/cloudflare.py +++ b/octodns/provider/cloudflare.py @@ -47,6 +47,16 @@ class CloudflareProvider(BaseProvider): # # See: https://support.cloudflare.com/hc/en-us/articles/115000830351 cdn: false + + Note: The "proxied" flag of "A", "AAAA" and "CNAME" records can be managed + via the YAML provider like so: + name: + octodons: + cloudflare: + proxied: true + ttl: 120 + type: A + value: 1.2.3.4 ''' SUPPORTS_GEO = False SUPPORTS = set(('ALIAS', 'A', 'AAAA', 'CAA', 'CNAME', 'MX', 'NS', 'SRV', From bcff231e3527098ad815f1e6c4d645bc2f7f35a2 Mon Sep 17 00:00:00 2001 From: Matt Date: Thu, 18 Oct 2018 21:25:43 +1100 Subject: [PATCH 5/5] Alter - New tests to be more unit-test-like. --- tests/test_octodns_provider_cloudflare.py | 647 ++++++++++------------ 1 file changed, 285 insertions(+), 362 deletions(-) diff --git a/tests/test_octodns_provider_cloudflare.py b/tests/test_octodns_provider_cloudflare.py index 544d0fb..f186309 100644 --- a/tests/test_octodns_provider_cloudflare.py +++ b/tests/test_octodns_provider_cloudflare.py @@ -769,141 +769,16 @@ class TestCloudflareProvider(TestCase): plan = provider.plan(wanted) self.assertEquals(False, hasattr(plan, 'changes')) - def test_proxied_ignore_ttl(self): + def test_unproxiabletype_recordfor_returnsrecordwithnocloudflare(self): provider = CloudflareProvider('test', 'email', 'token') - - provider.zone_records = Mock(return_value=[ + name = "unit.tests" + _type = "NS" + zone_records = [ { - "id": "fc12ab34cd5611334422ab3322997642", - "type": "A", - "name": "unit.tests.a", - "content": "1.2.3.4", - "proxiable": True, - "proxied": True, - "ttl": 1, - "locked": False, - "zone_id": "ff12ab34cd5611334422ab3322997650", - "zone_name": "unit.tests", - "modified_on": "2017-03-11T18:01:43.420689Z", - "created_on": "2017-03-11T18:01:43.420689Z", - "meta": { - "auto_added": False - } - }, - { - "id": "fc12ab34cd5611334422ab3322997643", - "type": "AAAA", - "name": "unit.tests.aaaa", - "content": "::1", - "proxiable": True, - "proxied": True, - "ttl": 1, - "locked": False, - "zone_id": "ff12ab34cd5611334422ab3322997650", - "zone_name": "unit.tests", - "modified_on": "2017-03-11T18:01:43.420689Z", - "created_on": "2017-03-11T18:01:43.420689Z", - "meta": { - "auto_added": False - } - }, - { - "id": "fc12ab34cd5611334422ab3322997644", - "type": "CNAME", - "name": "unit.tests.cname", - "content": "www.unit.tests", - "proxiable": True, - "proxied": True, - "ttl": 1, - "locked": False, - "zone_id": "ff12ab34cd5611334422ab3322997650", - "zone_name": "unit.tests", - "modified_on": "2017-03-11T18:01:43.420689Z", - "created_on": "2017-03-11T18:01:43.420689Z", - "meta": { - "auto_added": False - } - }, - ]) - - zone = Zone('unit.tests.', []) - provider.populate(zone) - - wanted = Zone('unit.tests.', []) - wanted.add_record( - set_record_proxied_flag( - Record.new(wanted, 'unit.tests.a', { - 'ttl': 120, - 'type': 'A', - 'value': '1.2.3.4' - }), True - ) - ) - wanted.add_record( - set_record_proxied_flag( - Record.new(wanted, 'unit.tests.aaaa', { - 'ttl': 250, - 'type': 'AAAA', - 'value': '::1' - }), True - ) - ) - wanted.add_record( - set_record_proxied_flag( - Record.new(wanted, 'unit.tests.cname', { - 'ttl': 300, - 'type': 'CNAME', - 'value': 'www.unit.tests.' - }), True - ) - ) - - plan = provider.plan(wanted) - self.assertEquals(False, hasattr(plan, 'changes')) - - def test_enable_proxied(self): - provider = CloudflareProvider('test', 'email', 'token') - - provider.zone_records = Mock(return_value=[ - { - "id": "fc12ab34cd5611334422ab3322997642", - "type": "A", - "name": "unit.tests.a", - "content": "1.2.3.4", - "proxiable": True, - "proxied": False, - "ttl": 120, - "locked": False, - "zone_id": "ff12ab34cd5611334422ab3322997650", - "zone_name": "unit.tests", - "modified_on": "2017-03-11T18:01:43.420689Z", - "created_on": "2017-03-11T18:01:43.420689Z", - "meta": { - "auto_added": False - } - }, - { - "id": "fc12ab34cd5611334422ab3322997643", - "type": "AAAA", - "name": "unit.tests.aaaa", - "content": "::1", - "proxiable": True, - "proxied": False, - "ttl": 250, - "locked": False, - "zone_id": "ff12ab34cd5611334422ab3322997650", - "zone_name": "unit.tests", - "modified_on": "2017-03-11T18:01:43.420689Z", - "created_on": "2017-03-11T18:01:43.420689Z", - "meta": { - "auto_added": False - } - }, - { - "id": "fc12ab34cd5611334422ab3322997644", - "type": "CNAME", - "name": "unit.tests.cname", - "content": "www.unit.tests", + "id": "fc12ab34cd5611334422ab3322997654", + "type": _type, + "name": name, + "content": "ns2.foo.bar", "proxiable": True, "proxied": False, "ttl": 300, @@ -915,56 +790,29 @@ class TestCloudflareProvider(TestCase): "meta": { "auto_added": False } - }, - ]) - + } + ] + provider.zone_records = Mock(return_value=zone_records) zone = Zone('unit.tests.', []) provider.populate(zone) - wanted = Zone('unit.tests.', []) - wanted.add_record( - set_record_proxied_flag( - Record.new(wanted, 'unit.tests.a', { - 'ttl': 120, - 'type': 'A', - 'value': '1.2.3.4' - }), True - ) - ) - wanted.add_record( - set_record_proxied_flag( - Record.new(wanted, 'unit.tests.aaaa', { - 'ttl': 250, - 'type': 'AAAA', - 'value': '::1' - }), True - ) - ) - wanted.add_record( - set_record_proxied_flag( - Record.new(wanted, 'unit.tests.cname', { - 'ttl': 300, - 'type': 'CNAME', - 'value': 'www.unit.tests.' - }), True - ) - ) + record = provider._record_for(zone, name, _type, zone_records, False) - plan = provider.plan(wanted) - self.assertEquals(True, hasattr(plan, 'changes')) + self.assertFalse('cloudflare' in record._octodns) - def test_disable_proxied(self): + def test_proxiabletype_recordfor_retrecordwithcloudflareunproxied(self): provider = CloudflareProvider('test', 'email', 'token') - - provider.zone_records = Mock(return_value=[ + name = "multi.unit.tests" + _type = "AAAA" + zone_records = [ { "id": "fc12ab34cd5611334422ab3322997642", - "type": "A", - "name": "unit.tests.a", - "content": "1.2.3.4", + "type": _type, + "name": name, + "content": "::1", "proxiable": True, - "proxied": True, - "ttl": 120, + "proxied": False, + "ttl": 300, "locked": False, "zone_id": "ff12ab34cd5611334422ab3322997650", "zone_name": "unit.tests", @@ -973,15 +821,29 @@ class TestCloudflareProvider(TestCase): "meta": { "auto_added": False } - }, + } + ] + provider.zone_records = Mock(return_value=zone_records) + zone = Zone('unit.tests.', []) + provider.populate(zone) + + record = provider._record_for(zone, name, _type, zone_records, False) + + self.assertFalse(record._octodns['cloudflare']['proxied']) + + def test_proxiabletype_recordfor_returnsrecordwithcloudflareproxied(self): + provider = CloudflareProvider('test', 'email', 'token') + name = "multi.unit.tests" + _type = "AAAA" + zone_records = [ { - "id": "fc12ab34cd5611334422ab3322997643", - "type": "AAAA", - "name": "unit.tests.aaaa", + "id": "fc12ab34cd5611334422ab3322997642", + "type": _type, + "name": name, "content": "::1", "proxiable": True, "proxied": True, - "ttl": 250, + "ttl": 300, "locked": False, "zone_id": "ff12ab34cd5611334422ab3322997650", "zone_name": "unit.tests", @@ -990,11 +852,90 @@ class TestCloudflareProvider(TestCase): "meta": { "auto_added": False } - }, + } + ] + provider.zone_records = Mock(return_value=zone_records) + zone = Zone('unit.tests.', []) + provider.populate(zone) + + record = provider._record_for(zone, name, _type, zone_records, False) + + self.assertTrue(record._octodns['cloudflare']['proxied']) + + def test_proxiedrecordandnewttl_includechange_returnsfalse(self): + provider = CloudflareProvider('test', 'email', 'token') + zone = Zone('unit.tests.', []) + existing = set_record_proxied_flag( + Record.new(zone, 'a', { + 'ttl': 1, + 'type': 'A', + 'values': ['1.1.1.1', '2.2.2.2'] + }), True + ) + new = Record.new(zone, 'a', { + 'ttl': 300, + 'type': 'A', + 'values': ['1.1.1.1', '2.2.2.2'] + }) + change = Update(existing, new) + + include_change = provider._include_change(change) + + self.assertFalse(include_change) + + def test_unproxiabletype_gendata_returnsnoproxied(self): + provider = CloudflareProvider('test', 'email', 'token') + zone = Zone('unit.tests.', []) + record = Record.new(zone, 'a', { + 'ttl': 3600, + 'type': 'NS', + 'value': 'ns1.unit.tests.' + }) + + data = provider._gen_data(record).next() + + self.assertFalse('proxied' in data) + + def test_proxiabletype_gendata_returnsunproxied(self): + provider = CloudflareProvider('test', 'email', 'token') + zone = Zone('unit.tests.', []) + record = set_record_proxied_flag( + Record.new(zone, 'a', { + 'ttl': 300, + 'type': 'A', + 'value': '1.2.3.4' + }), False + ) + + data = provider._gen_data(record).next() + + self.assertFalse(data['proxied']) + + def test_proxiabletype_gendata_returnsproxied(self): + provider = CloudflareProvider('test', 'email', 'token') + zone = Zone('unit.tests.', []) + record = set_record_proxied_flag( + Record.new(zone, 'a', { + 'ttl': 300, + 'type': 'A', + 'value': '1.2.3.4' + }), True + ) + + data = provider._gen_data(record).next() + + self.assertTrue(data['proxied']) + + def test_createrecord_extrachanges_returnsemptylist(self): + provider = CloudflareProvider('test', 'email', 'token') + provider.zone_records = Mock(return_value=[]) + existing = Zone('unit.tests.', []) + provider.populate(existing) + provider.zone_records = Mock(return_value=[ { - "id": "fc12ab34cd5611334422ab3322997644", + "id": "fc12ab34cd5611334422ab3322997642", "type": "CNAME", - "name": "unit.tests.cname", + "name": "a.unit.tests", "content": "www.unit.tests", "proxiable": True, "proxied": True, @@ -1007,146 +948,25 @@ class TestCloudflareProvider(TestCase): "meta": { "auto_added": False } - }, + } ]) + desired = Zone('unit.tests.', []) + provider.populate(desired) + changes = existing.changes(desired, provider) - zone = Zone('unit.tests.', []) - provider.populate(zone) + extra_changes = provider._extra_changes(existing, desired, changes) - wanted = Zone('unit.tests.', []) - wanted.add_record( - set_record_proxied_flag( - Record.new(wanted, 'unit.tests.a', { - 'ttl': 120, - 'type': 'A', - 'value': '1.2.3.4' - }), False - ) - ) - wanted.add_record( - set_record_proxied_flag( - Record.new(wanted, 'unit.tests.aaaa', { - 'ttl': 250, - 'type': 'AAAA', - 'value': '::1' - }), False - ) - ) - wanted.add_record( - set_record_proxied_flag( - Record.new(wanted, 'unit.tests.cname', { - 'ttl': 300, - 'type': 'CNAME', - 'value': 'www.unit.tests.' - }), False - ) - ) + self.assertFalse(extra_changes) - plan = provider.plan(wanted) - self.assertEquals(True, hasattr(plan, 'changes')) - - def test_leave_proxied_disabled(self): + def test_updaterecord_extrachanges_returnsemptylist(self): provider = CloudflareProvider('test', 'email', 'token') - provider.zone_records = Mock(return_value=[ { "id": "fc12ab34cd5611334422ab3322997642", - "type": "A", - "name": "unit.tests.a", - "content": "1.2.3.4", - "proxiable": True, - "proxied": False, - "ttl": 120, - "locked": False, - "zone_id": "ff12ab34cd5611334422ab3322997650", - "zone_name": "unit.tests", - "modified_on": "2017-03-11T18:01:43.420689Z", - "created_on": "2017-03-11T18:01:43.420689Z", - "meta": { - "auto_added": False - } - }, - { - "id": "fc12ab34cd5611334422ab3322997643", - "type": "AAAA", - "name": "unit.tests.aaaa", - "content": "::1", - "proxiable": True, - "proxied": False, - "ttl": 250, - "locked": False, - "zone_id": "ff12ab34cd5611334422ab3322997650", - "zone_name": "unit.tests", - "modified_on": "2017-03-11T18:01:43.420689Z", - "created_on": "2017-03-11T18:01:43.420689Z", - "meta": { - "auto_added": False - } - }, - { - "id": "fc12ab34cd5611334422ab3322997644", "type": "CNAME", - "name": "unit.tests.cname", + "name": "a.unit.tests", "content": "www.unit.tests", "proxiable": True, - "proxied": False, - "ttl": 300, - "locked": False, - "zone_id": "ff12ab34cd5611334422ab3322997650", - "zone_name": "unit.tests", - "modified_on": "2017-03-11T18:01:43.420689Z", - "created_on": "2017-03-11T18:01:43.420689Z", - "meta": { - "auto_added": False - } - }, - ]) - - zone = Zone('unit.tests.', []) - provider.populate(zone) - - wanted = Zone('unit.tests.', []) - wanted.add_record( - set_record_proxied_flag( - Record.new(wanted, 'unit.tests.a', { - 'ttl': 120, - 'type': 'A', - 'value': '1.2.3.4' - }), False - ) - ) - wanted.add_record( - set_record_proxied_flag( - Record.new(wanted, 'unit.tests.aaaa', { - 'ttl': 250, - 'type': 'AAAA', - 'value': '::1' - }), False - ) - ) - wanted.add_record( - set_record_proxied_flag( - Record.new(wanted, 'unit.tests.cname', { - 'ttl': 300, - 'type': 'CNAME', - 'value': 'www.unit.tests.' - }), False - ) - ) - - plan = provider.plan(wanted) - self.assertEquals(False, hasattr(plan, 'changes')) - - def test_leave_proxied_enabled(self): - provider = CloudflareProvider('test', 'email', 'token') - - provider.zone_records = Mock(return_value=[ - { - "id": "fc12ab34cd5611334422ab3322997642", - "type": "A", - "name": "unit.tests.a", - "content": "1.2.3.4", - "proxiable": True, "proxied": True, "ttl": 120, "locked": False, @@ -1157,28 +977,15 @@ class TestCloudflareProvider(TestCase): "meta": { "auto_added": False } - }, + } + ]) + existing = Zone('unit.tests.', []) + provider.populate(existing) + provider.zone_records = Mock(return_value=[ { - "id": "fc12ab34cd5611334422ab3322997643", - "type": "AAAA", - "name": "unit.tests.aaaa", - "content": "::1", - "proxiable": True, - "proxied": True, - "ttl": 250, - "locked": False, - "zone_id": "ff12ab34cd5611334422ab3322997650", - "zone_name": "unit.tests", - "modified_on": "2017-03-11T18:01:43.420689Z", - "created_on": "2017-03-11T18:01:43.420689Z", - "meta": { - "auto_added": False - } - }, - { - "id": "fc12ab34cd5611334422ab3322997644", + "id": "fc12ab34cd5611334422ab3322997642", "type": "CNAME", - "name": "unit.tests.cname", + "name": "a.unit.tests", "content": "www.unit.tests", "proxiable": True, "proxied": True, @@ -1191,40 +998,156 @@ class TestCloudflareProvider(TestCase): "meta": { "auto_added": False } - }, + } ]) + desired = Zone('unit.tests.', []) + provider.populate(desired) + changes = existing.changes(desired, provider) - zone = Zone('unit.tests.', []) - provider.populate(zone) + extra_changes = provider._extra_changes(existing, desired, changes) - wanted = Zone('unit.tests.', []) - wanted.add_record( - set_record_proxied_flag( - Record.new(wanted, 'unit.tests.a', { - 'ttl': 120, - 'type': 'A', - 'value': '1.2.3.4' - }), True - ) + self.assertFalse(extra_changes) + + def test_deleterecord_extrachanges_returnsemptylist(self): + provider = CloudflareProvider('test', 'email', 'token') + provider.zone_records = Mock(return_value=[ + { + "id": "fc12ab34cd5611334422ab3322997642", + "type": "CNAME", + "name": "a.unit.tests", + "content": "www.unit.tests", + "proxiable": True, + "proxied": True, + "ttl": 300, + "locked": False, + "zone_id": "ff12ab34cd5611334422ab3322997650", + "zone_name": "unit.tests", + "modified_on": "2017-03-11T18:01:43.420689Z", + "created_on": "2017-03-11T18:01:43.420689Z", + "meta": { + "auto_added": False + } + } + ]) + existing = Zone('unit.tests.', []) + provider.populate(existing) + provider.zone_records = Mock(return_value=[]) + desired = Zone('unit.tests.', []) + provider.populate(desired) + changes = existing.changes(desired, provider) + + extra_changes = provider._extra_changes(existing, desired, changes) + + self.assertFalse(extra_changes) + + def test_proxify_extrachanges_returnsupdatelist(self): + provider = CloudflareProvider('test', 'email', 'token') + provider.zone_records = Mock(return_value=[ + { + "id": "fc12ab34cd5611334422ab3322997642", + "type": "CNAME", + "name": "a.unit.tests", + "content": "www.unit.tests", + "proxiable": True, + "proxied": False, + "ttl": 300, + "locked": False, + "zone_id": "ff12ab34cd5611334422ab3322997650", + "zone_name": "unit.tests", + "modified_on": "2017-03-11T18:01:43.420689Z", + "created_on": "2017-03-11T18:01:43.420689Z", + "meta": { + "auto_added": False + } + } + ]) + existing = Zone('unit.tests.', []) + provider.populate(existing) + provider.zone_records = Mock(return_value=[ + { + "id": "fc12ab34cd5611334422ab3322997642", + "type": "CNAME", + "name": "a.unit.tests", + "content": "www.unit.tests", + "proxiable": True, + "proxied": True, + "ttl": 300, + "locked": False, + "zone_id": "ff12ab34cd5611334422ab3322997650", + "zone_name": "unit.tests", + "modified_on": "2017-03-11T18:01:43.420689Z", + "created_on": "2017-03-11T18:01:43.420689Z", + "meta": { + "auto_added": False + } + } + ]) + desired = Zone('unit.tests.', []) + provider.populate(desired) + changes = existing.changes(desired, provider) + + extra_changes = provider._extra_changes(existing, desired, changes) + + self.assertEquals(1, len(extra_changes)) + self.assertFalse( + extra_changes[0].existing._octodns['cloudflare']['proxied'] ) - wanted.add_record( - set_record_proxied_flag( - Record.new(wanted, 'unit.tests.aaaa', { - 'ttl': 250, - 'type': 'AAAA', - 'value': '::1' - }), True - ) - ) - wanted.add_record( - set_record_proxied_flag( - Record.new(wanted, 'unit.tests.cname', { - 'ttl': 300, - 'type': 'CNAME', - 'value': 'www.unit.tests.' - }), True - ) + self.assertTrue( + extra_changes[0].new._octodns['cloudflare']['proxied'] ) - plan = provider.plan(wanted) - self.assertEquals(False, hasattr(plan, 'changes')) + def test_unproxify_extrachanges_returnsupdatelist(self): + provider = CloudflareProvider('test', 'email', 'token') + provider.zone_records = Mock(return_value=[ + { + "id": "fc12ab34cd5611334422ab3322997642", + "type": "CNAME", + "name": "a.unit.tests", + "content": "www.unit.tests", + "proxiable": True, + "proxied": True, + "ttl": 300, + "locked": False, + "zone_id": "ff12ab34cd5611334422ab3322997650", + "zone_name": "unit.tests", + "modified_on": "2017-03-11T18:01:43.420689Z", + "created_on": "2017-03-11T18:01:43.420689Z", + "meta": { + "auto_added": False + } + } + ]) + existing = Zone('unit.tests.', []) + provider.populate(existing) + provider.zone_records = Mock(return_value=[ + { + "id": "fc12ab34cd5611334422ab3322997642", + "type": "CNAME", + "name": "a.unit.tests", + "content": "www.unit.tests", + "proxiable": True, + "proxied": False, + "ttl": 300, + "locked": False, + "zone_id": "ff12ab34cd5611334422ab3322997650", + "zone_name": "unit.tests", + "modified_on": "2017-03-11T18:01:43.420689Z", + "created_on": "2017-03-11T18:01:43.420689Z", + "meta": { + "auto_added": False + } + } + ]) + desired = Zone('unit.tests.', []) + provider.populate(desired) + changes = existing.changes(desired, provider) + + extra_changes = provider._extra_changes(existing, desired, changes) + + self.assertEquals(1, len(extra_changes)) + self.assertTrue( + extra_changes[0].existing._octodns['cloudflare']['proxied'] + ) + self.assertFalse( + extra_changes[0].new._octodns['cloudflare']['proxied'] + )