Exemplo n.º 1
0
    def test_retry_behavior(self):
        provider = CloudflareProvider('test', token='token 123',
                                      email='email 234', retry_period=0)
        result = {
            "success": True,
            "errors": [],
            "messages": [],
            "result": [],
            "result_info": {
                "count": 1,
                "per_page": 50
            }
        }
        zone = Zone('unit.tests.', [])
        provider._request = Mock()

        # No retry required, just calls and is returned
        provider._zones = None
        provider._request.reset_mock()
        provider._request.side_effect = [result]
        self.assertEquals([], provider.zone_records(zone))
        provider._request.assert_has_calls([call('GET', '/zones',
                                           params={'page': 1})])

        # One retry required
        provider._zones = None
        provider._request.reset_mock()
        provider._request.side_effect = [
            CloudflareRateLimitError('{}'),
            result
        ]
        self.assertEquals([], provider.zone_records(zone))
        provider._request.assert_has_calls([call('GET', '/zones',
                                           params={'page': 1})])

        # Two retries required
        provider._zones = None
        provider._request.reset_mock()
        provider._request.side_effect = [
            CloudflareRateLimitError('{}'),
            CloudflareRateLimitError('{}'),
            result
        ]
        self.assertEquals([], provider.zone_records(zone))
        provider._request.assert_has_calls([call('GET', '/zones',
                                           params={'page': 1})])

        # # Exhaust our retries
        provider._zones = None
        provider._request.reset_mock()
        provider._request.side_effect = [
            CloudflareRateLimitError({"errors": [{"message": "first"}]}),
            CloudflareRateLimitError({"errors": [{"message": "boo"}]}),
            CloudflareRateLimitError({"errors": [{"message": "boo"}]}),
            CloudflareRateLimitError({"errors": [{"message": "boo"}]}),
            CloudflareRateLimitError({"errors": [{"message": "last"}]}),
        ]
        with self.assertRaises(CloudflareRateLimitError) as ctx:
            provider.zone_records(zone)
            self.assertEquals('last', text_type(ctx.exception))
    def test_update_delete(self):
        # We need another run so that we can delete, we can't both add and
        # delete in one go b/c of swaps
        provider = CloudflareProvider('test', 'email', 'token')

        provider.zone_records = Mock(return_value=[
            {
                "id": "fc12ab34cd5611334422ab3322997653",
                "type": "NS",
                "name": "unit.tests",
                "content": "ns1.foo.bar",
                "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
                }
            },
            {
                "id": "fc12ab34cd5611334422ab3322997654",
                "type": "NS",
                "name": "unit.tests",
                "content": "ns2.foo.bar",
                "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
                }
            },
        ])

        provider._request = Mock()
        provider._request.side_effect = [
            self.empty,  # no zones
            {
                'result': {
                    'id': 42,
                }
            },  # zone create
            None,
            None,
        ]

        # Add something and delete something
        zone = Zone('unit.tests.', [])
        existing = Record.new(
            zone,
            '',
            {
                'ttl': 300,
                'type': 'NS',
                # This matches the zone data above, one to delete, one to leave
                'values': ['ns1.foo.bar.', 'ns2.foo.bar.'],
            })
        new = Record.new(
            zone,
            '',
            {
                'ttl': 300,
                'type': 'NS',
                # This leaves one and deletes one
                'value': 'ns2.foo.bar.',
            })
        change = Update(existing, new)
        plan = Plan(zone, zone, [change])
        provider._apply(plan)

        provider._request.assert_has_calls([
            call('GET', '/zones', params={'page': 1}),
            call('POST',
                 '/zones',
                 data={
                     'jump_start': False,
                     'name': 'unit.tests'
                 }),
            call(
                'DELETE', '/zones/ff12ab34cd5611334422ab3322997650/'
                'dns_records/fc12ab34cd5611334422ab3322997653')
        ])
    def test_update_add_swap(self):
        provider = CloudflareProvider('test', 'email', 'token')

        provider.zone_records = Mock(return_value=[
            {
                "id": "fc12ab34cd5611334422ab3322997653",
                "type": "A",
                "name": "a.unit.tests",
                "content": "1.1.1.1",
                "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
                }
            },
            {
                "id": "fc12ab34cd5611334422ab3322997654",
                "type": "A",
                "name": "a.unit.tests",
                "content": "2.2.2.2",
                "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
                }
            },
        ])

        provider._request = Mock()
        provider._request.side_effect = [
            self.empty,  # no zones
            {
                'result': {
                    'id': 42,
                }
            },  # zone create
            None,
            None,
        ]

        # Add something and delete something
        zone = Zone('unit.tests.', [])
        existing = Record.new(
            zone,
            'a',
            {
                'ttl': 300,
                'type': 'A',
                # This matches the zone data above, one to swap, one to leave
                'values': ['1.1.1.1', '2.2.2.2'],
            })
        new = Record.new(
            zone,
            'a',
            {
                'ttl': 300,
                'type': 'A',
                # This leaves one, swaps ones, and adds one
                'values': ['2.2.2.2', '3.3.3.3', '4.4.4.4'],
            })
        change = Update(existing, new)
        plan = Plan(zone, zone, [change])
        provider._apply(plan)

        provider._request.assert_has_calls([
            call('GET', '/zones', params={'page': 1}),
            call('POST',
                 '/zones',
                 data={
                     'jump_start': False,
                     'name': 'unit.tests'
                 }),
            call('PUT', '/zones/ff12ab34cd5611334422ab3322997650/dns_records/'
                 'fc12ab34cd5611334422ab3322997653',
                 data={
                     'content': '4.4.4.4',
                     'type': 'A',
                     'name': 'a.unit.tests',
                     'ttl': 300
                 }),
            call('POST',
                 '/zones/42/dns_records',
                 data={
                     'content': '3.3.3.3',
                     'type': 'A',
                     'name': 'a.unit.tests',
                     'ttl': 300
                 })
        ])
    def test_apply(self):
        provider = CloudflareProvider('test', 'email', 'token')

        provider._request = Mock()

        provider._request.side_effect = [
            self.empty,  # no zones
            {
                'result': {
                    'id': 42,
                }
            },  # zone create
        ] + [None] * 20  # individual record creates

        # non-existant zone, create everything
        plan = provider.plan(self.expected)
        self.assertEquals(12, len(plan.changes))
        self.assertEquals(12, provider.apply(plan))

        provider._request.assert_has_calls(
            [
                # created the domain
                call('POST',
                     '/zones',
                     data={
                         'jump_start': False,
                         'name': 'unit.tests'
                     }),
                # created at least one of the record with expected data
                call('POST',
                     '/zones/42/dns_records',
                     data={
                         'content': 'ns1.unit.tests.',
                         'type': 'NS',
                         'name': 'under.unit.tests',
                         'ttl': 3600
                     }),
                # make sure semicolons are not escaped when sending data
                call('POST',
                     '/zones/42/dns_records',
                     data={
                         'content': 'v=DKIM1;k=rsa;s=email;h=sha256;'
                         'p=A/kinda+of/long/string+with+numb3rs',
                         'type': 'TXT',
                         'name': 'txt.unit.tests',
                         'ttl': 600
                     }),
            ],
            True)
        # expected number of total calls
        self.assertEquals(22, provider._request.call_count)

        provider._request.reset_mock()

        provider.zone_records = Mock(return_value=[
            {
                "id": "fc12ab34cd5611334422ab3322997653",
                "type": "A",
                "name": "www.unit.tests",
                "content": "1.2.3.4",
                "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
                }
            },
            {
                "id": "fc12ab34cd5611334422ab3322997654",
                "type": "A",
                "name": "www.unit.tests",
                "content": "2.2.3.4",
                "proxiable": True,
                "proxied": False,
                "ttl": 300,
                "locked": False,
                "zone_id": "ff12ab34cd5611334422ab3322997650",
                "zone_name": "unit.tests",
                "modified_on": "2017-03-11T18:01:44.030044Z",
                "created_on": "2017-03-11T18:01:44.030044Z",
                "meta": {
                    "auto_added": False
                }
            },
            {
                "id": "fc12ab34cd5611334422ab3322997655",
                "type": "A",
                "name": "nc.unit.tests",
                "content": "3.2.3.4",
                "proxiable": True,
                "proxied": False,
                "ttl": 120,
                "locked": False,
                "zone_id": "ff12ab34cd5611334422ab3322997650",
                "zone_name": "unit.tests",
                "modified_on": "2017-03-11T18:01:44.030044Z",
                "created_on": "2017-03-11T18:01:44.030044Z",
                "meta": {
                    "auto_added": False
                }
            },
            {
                "id": "fc12ab34cd5611334422ab3322997655",
                "type": "A",
                "name": "ttl.unit.tests",
                "content": "4.2.3.4",
                "proxiable": True,
                "proxied": False,
                "ttl": 600,
                "locked": False,
                "zone_id": "ff12ab34cd5611334422ab3322997650",
                "zone_name": "unit.tests",
                "modified_on": "2017-03-11T18:01:44.030044Z",
                "created_on": "2017-03-11T18:01:44.030044Z",
                "meta": {
                    "auto_added": False
                }
            },
        ])

        # we don't care about the POST/create return values
        provider._request.return_value = {}
        provider._request.side_effect = None

        wanted = Zone('unit.tests.', [])
        wanted.add_record(
            Record.new(
                wanted,
                'nc',
                {
                    'ttl': 60,  # TTL is below their min
                    'type': 'A',
                    'value': '3.2.3.4'
                }))
        wanted.add_record(
            Record.new(
                wanted,
                'ttl',
                {
                    'ttl': 300,  # TTL change
                    'type': 'A',
                    'value': '3.2.3.4'
                }))

        plan = provider.plan(wanted)
        # only see the delete & ttl update, below min-ttl is filtered out
        self.assertEquals(2, len(plan.changes))
        self.assertEquals(2, provider.apply(plan))
        # recreate for update, and deletes for the 2 parts of the other
        provider._request.assert_has_calls([
            call('PUT', '/zones/ff12ab34cd5611334422ab3322997650/dns_records/'
                 'fc12ab34cd5611334422ab3322997655',
                 data={
                     'content': '3.2.3.4',
                     'type': 'A',
                     'name': 'ttl.unit.tests',
                     'ttl': 300
                 }),
            call(
                'DELETE', '/zones/ff12ab34cd5611334422ab3322997650/'
                'dns_records/fc12ab34cd5611334422ab3322997653'),
            call(
                'DELETE', '/zones/ff12ab34cd5611334422ab3322997650/'
                'dns_records/fc12ab34cd5611334422ab3322997654')
        ])