def test_apply(self):
        provider = self._get_provider()

        changes = []
        deletes = []
        for i in octo_records:
            changes.append(Create(i))
            deletes.append(Delete(i))

        self.assertEquals(13, provider.apply(Plan(None, zone, changes)))
        self.assertEquals(13, provider.apply(Plan(zone, zone, deletes)))
Esempio n. 2
0
    def test_safe_deletes_min_existing_override(self):
        safe_pcent = .4
        # 40% + 1 fails when more
        # than MIN_EXISTING_RECORDS exist
        zone = Zone('unit.tests.', [])
        record = Record.new(zone, 'a', {
            'ttl': 30,
            'type': 'A',
            'value': '1.2.3.4',
        })

        for i in range(int(Plan.MIN_EXISTING_RECORDS)):
            zone.add_record(
                Record.new(zone, str(i), {
                    'ttl': 60,
                    'type': 'A',
                    'value': '2.3.4.5'
                }))

        changes = [
            Delete(record)
            for i in range(int(Plan.MIN_EXISTING_RECORDS * safe_pcent) + 1)
        ]

        with self.assertRaises(UnsafePlan) as ctx:
            Plan(zone, zone, changes,
                 delete_pcent_threshold=safe_pcent).raise_if_unsafe()

        self.assertTrue('Too many deletes' in ctx.exception.message)
Esempio n. 3
0
    def test_safe_updates_min_existing(self):
        # MAX_SAFE_UPDATE_PCENT+1 fails when more
        # than MIN_EXISTING_RECORDS exist
        zone = Zone('unit.tests.', [])
        record = Record.new(zone, 'a', {
            'ttl': 30,
            'type': 'A',
            'value': '1.2.3.4',
        })

        for i in range(int(Plan.MIN_EXISTING_RECORDS)):
            zone.add_record(
                Record.new(zone, str(i), {
                    'ttl': 60,
                    'type': 'A',
                    'value': '2.3.4.5'
                }))

        changes = [
            Update(record, record) for i in range(
                int(Plan.MIN_EXISTING_RECORDS * Plan.MAX_SAFE_UPDATE_PCENT) +
                1)
        ]

        with self.assertRaises(UnsafePlan) as ctx:
            Plan(zone, zone, changes).raise_if_unsafe()

        self.assertTrue('Too many updates' in ctx.exception.message)
    def test_safe_deletes_min_existing(self):
        # MAX_SAFE_DELETE_PCENT+1 fails when more
        # than MIN_EXISTING_RECORDS exist
        zone = Zone('unit.tests.', [])
        record = Record.new(zone, 'a', {
            'ttl': 30,
            'type': 'A',
            'value': '1.2.3.4',
        })

        for i in range(int(Plan.MIN_EXISTING_RECORDS)):
            zone.add_record(
                Record.new(zone, str(i), {
                    'ttl': 60,
                    'type': 'A',
                    'value': '2.3.4.5'
                }))

        changes = [
            Delete(record) for i in range(
                int(Plan.MIN_EXISTING_RECORDS * Plan.MAX_SAFE_DELETE_PCENT) +
                1)
        ]

        with self.assertRaises(UnsafePlan):
            Plan(zone, zone, changes).raise_if_unsafe()
Esempio n. 5
0
    def test_apply_handles_existing_zone_directory(self):
        with TemporaryDirectory() as td:
            provider = SplitYamlProvider('test', join(td.dirname, 'config'))
            makedirs(join(td.dirname, 'config', 'does.exist.'))

            zone = Zone('does.exist.', [])
            self.assertTrue(isdir(provider._zone_directory(zone)))
            provider.apply(Plan(None, zone, [], True))
            self.assertTrue(isdir(provider._zone_directory(zone)))
Esempio n. 6
0
    def test_safe_creates(self):
        # Creates are safe when existing records is under MIN_EXISTING_RECORDS
        zone = Zone('unit.tests.', [])

        record = Record.new(zone, 'a', {
            'ttl': 30,
            'type': 'A',
            'value': '1.2.3.4',
        })
        Plan(zone, zone, [Create(record) for i in range(10)]).raise_if_unsafe()
Esempio n. 7
0
    def test_safe_no_existing(self):
        # existing records fewer than MIN_EXISTING_RECORDS is safe
        zone = Zone('unit.tests.', [])
        record = Record.new(zone, 'a', {
            'ttl': 30,
            'type': 'A',
            'value': '1.2.3.4',
        })

        updates = [Update(record, record), Update(record, record)]
        Plan(zone, zone, updates).raise_if_unsafe()
Esempio n. 8
0
    def test_safe(self):
        ignored = Zone('unit.tests.', [])

        # No changes is safe
        Plan(None, None, []).raise_if_unsafe()

        # Creates are safe
        record = Record.new(ignored, 'a', {
            'ttl': 30,
            'type': 'A',
            'value': '1.2.3.4',
        })
        Plan(None, None, [Create(record) for i in range(10)]).raise_if_unsafe()

        # max Updates is safe
        changes = [
            Update(record, record) for i in range(Plan.MAX_SAFE_UPDATES)
        ]
        Plan(None, None, changes).raise_if_unsafe()
        # but max + 1 isn't
        with self.assertRaises(UnsafePlan):
            changes.append(Update(record, record))
            Plan(None, None, changes).raise_if_unsafe()

        # max Deletes is safe
        changes = [Delete(record) for i in range(Plan.MAX_SAFE_DELETES)]
        Plan(None, None, changes).raise_if_unsafe()
        # but max + 1 isn't
        with self.assertRaises(UnsafePlan):
            changes.append(Delete(record))
            Plan(None, None, changes).raise_if_unsafe()
Esempio n. 9
0
    def test_apply_traffic_director(self, mock):
        provider = DynProvider('test',
                               'cust',
                               'user',
                               'pass',
                               traffic_directors_enabled=True)

        # stubbing these out to avoid a lot of messy mocking, they'll be tested
        # individually, we'll check for expected calls
        provider._mod_geo_Create = MagicMock()
        provider._mod_geo_Update = MagicMock()
        provider._mod_geo_Delete = MagicMock()
        provider._mod_Create = MagicMock()
        provider._mod_Update = MagicMock()
        provider._mod_Delete = MagicMock()

        # busted traffic director
        mock.side_effect = [
            # get zone
            {
                'data': {}
            },
            # accept publish
            {
                'data': {}
            },
        ]
        desired = Zone('unit.tests.', [])
        geo = self.geo_record
        regular = self.regular_record

        changes = [
            Create(geo),
            Create(regular),
            Update(geo, geo),
            Update(regular, regular),
            Delete(geo),
            Delete(regular),
        ]
        plan = Plan(None, desired, changes)
        provider._apply(plan)
        mock.assert_has_calls([
            call('/Zone/unit.tests/', 'GET', {}),
            call('/Zone/unit.tests/', 'PUT', {'publish': True})
        ])
        # should have seen 1 call to each
        provider._mod_geo_Create.assert_called_once()
        provider._mod_geo_Update.assert_called_once()
        provider._mod_geo_Delete.assert_called_once()
        provider._mod_Create.assert_called_once()
        provider._mod_Update.assert_called_once()
        provider._mod_Delete.assert_called_once()
    def test_create_zone(self):
        provider = self._get_provider()

        changes = []
        for i in octo_records:
            changes.append(Create(i))
        desired = Zone('unit2.test.', [])

        err_msg = 'The Resource \'Microsoft.Network/dnszones/unit2.test\' '
        err_msg += 'under resource group \'mock_rg\' was not found.'
        _get = provider._dns_client.zones.get
        _get.side_effect = CloudError(Mock(status=404), err_msg)

        self.assertEquals(13, provider.apply(Plan(None, desired, changes)))
Esempio n. 11
0
    def test_safe_min_existing_creates(self):
        # Creates are safe when existing records is over MIN_EXISTING_RECORDS
        zone = Zone('unit.tests.', [])

        record = Record.new(zone, 'a', {
            'ttl': 30,
            'type': 'A',
            'value': '1.2.3.4',
        })

        for i in range(int(Plan.MIN_EXISTING_RECORDS)):
            zone.add_record(Record.new(zone, str(i), {
                            'ttl': 60,
                            'type': 'A',
                            'value': '2.3.4.5'
                            }))

        Plan(zone, zone, [Create(record) for i in range(10)]).raise_if_unsafe()
Esempio n. 12
0
    def test_safe_updates_min_existing_pcent(self):
        # MAX_SAFE_UPDATE_PCENT is safe when more
        # than MIN_EXISTING_RECORDS exist
        zone = Zone('unit.tests.', [])
        record = Record.new(zone, 'a', {
            'ttl': 30,
            'type': 'A',
            'value': '1.2.3.4',
        })

        for i in range(int(Plan.MIN_EXISTING_RECORDS)):
            zone.add_record(Record.new(zone, str(i), {
                            'ttl': 60,
                            'type': 'A',
                            'value': '2.3.4.5'
                            }))
        changes = [Update(record, record)
                   for i in range(int(Plan.MIN_EXISTING_RECORDS *
                                      Plan.MAX_SAFE_UPDATE_PCENT))]

        Plan(zone, zone, changes).raise_if_unsafe()
    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
                 })
        ])
Esempio n. 15
0
 def test_safe_none(self):
     # No changes is safe
     Plan(None, None, []).raise_if_unsafe()
Esempio n. 16
0
    def test__apply(self, *_):
        class DummyDesired:
            def __init__(self, name, changes):
                self.name = name
                self.changes = changes

        apply_z = Zone("unit.tests.", [])
        create_r = Record.new(apply_z, '', {
            'ttl': 0,
            'type': 'A',
            'values': ['1.2.3.4', '10.10.10.10']
        })
        delete_r = Record.new(apply_z, 'a', {
            'ttl': 1,
            'type': 'A',
            'values': ['1.2.3.4', '1.1.1.1']
        })
        update_existing_r = Record.new(apply_z, 'aa', {
            'ttl': 9001,
            'type': 'A',
            'values': ['1.2.4.3']
        })
        update_new_r = Record.new(apply_z, 'aa', {
            'ttl': 666,
            'type': 'A',
            'values': ['1.4.3.2']
        })

        gcloud_zone_mock = DummyGoogleCloudZone("unit.tests.", "unit-tests")
        status_mock = Mock()
        return_values_for_status = iter(["pending"] * 11 + ['done', 'done'])
        type(status_mock).status = PropertyMock(
            side_effect=return_values_for_status.next)
        gcloud_zone_mock.changes = Mock(return_value=status_mock)

        provider = self._get_provider()
        provider.gcloud_client = Mock()
        provider._gcloud_zones = {"unit.tests.": gcloud_zone_mock}
        desired = Mock()
        desired.name = "unit.tests."
        changes = []
        changes.append(Create(create_r))
        changes.append(Delete(delete_r))
        changes.append(Update(existing=update_existing_r, new=update_new_r))

        provider.apply(
            Plan(existing=[update_existing_r, delete_r],
                 desired=desired,
                 changes=changes))

        calls_mock = gcloud_zone_mock.changes.return_value
        mocked_calls = []
        for mock_call in calls_mock.add_record_set.mock_calls:
            mocked_calls.append(mock_call[1][0])

        self.assertEqual(mocked_calls, [
            DummyResourceRecordSet('unit.tests.', 'A', 0,
                                   ['1.2.3.4', '10.10.10.10']),
            DummyResourceRecordSet('aa.unit.tests.', 'A', 666, ['1.4.3.2'])
        ])

        mocked_calls2 = []
        for mock_call in calls_mock.delete_record_set.mock_calls:
            mocked_calls2.append(mock_call[1][0])

        self.assertEqual(mocked_calls2, [
            DummyResourceRecordSet('a.unit.tests.', 'A', 1,
                                   ['1.2.3.4', '1.1.1.1']),
            DummyResourceRecordSet('aa.unit.tests.', 'A', 9001, ['1.2.4.3'])
        ])

        type(status_mock).status = "pending"

        with self.assertRaises(RuntimeError):
            provider.apply(
                Plan(existing=[update_existing_r, delete_r],
                     desired=desired,
                     changes=changes))

        unsupported_change = Mock()
        unsupported_change.__len__ = Mock(return_value=1)
        type_mock = Mock()
        type_mock._type = "A"
        unsupported_change.record = type_mock

        mock_plan = Mock()
        type(mock_plan).desired = PropertyMock(
            return_value=DummyDesired("dummy name", []))
        type(mock_plan).changes = [unsupported_change]

        with self.assertRaises(RuntimeError):
            provider.apply(mock_plan)