def test_escaping(self):
        provider = Ns1Provider('test', 'api-key')
        record = {
            'ttl': 31,
            'short_answers': ['foo; bar baz; blip']
        }
        self.assertEquals(['foo\; bar baz\; blip'],
                          provider._data_for_SPF('SPF', record)['values'])

        record = {
            'ttl': 31,
            'short_answers': ['no', 'foo; bar baz; blip', 'yes']
        }
        self.assertEquals(['no', 'foo\; bar baz\; blip', 'yes'],
                          provider._data_for_TXT('TXT', record)['values'])

        zone = Zone('unit.tests.', [])
        record = Record.new(zone, 'spf', {
            'ttl': 34,
            'type': 'SPF',
            'value': 'foo\; bar baz\; blip'
        })
        self.assertEquals(['foo; bar baz; blip'],
                          provider._params_for_SPF(record)['answers'])

        record = Record.new(zone, 'txt', {
            'ttl': 35,
            'type': 'TXT',
            'value': 'foo\; bar baz\; blip'
        })
        self.assertEquals(['foo; bar baz; blip'],
                          provider._params_for_TXT(record)['answers'])
    def test_data_for_CNAME(self):
        provider = Ns1Provider('test', 'api-key')

        # answers from nsone
        a_record = {
            'ttl': 31,
            'type': 'CNAME',
            'short_answers': ['foo.unit.tests.']
        }
        a_expected = {
            'ttl': 31,
            'type': 'CNAME',
            'value': 'foo.unit.tests.'
        }
        self.assertEqual(a_expected,
                         provider._data_for_CNAME(a_record['type'], a_record))

        # no answers from nsone
        b_record = {
            'ttl': 32,
            'type': 'CNAME',
            'short_answers': []
        }
        b_expected = {
            'ttl': 32,
            'type': 'CNAME',
            'value': None
        }
        self.assertEqual(b_expected,
                         provider._data_for_CNAME(b_record['type'], b_record))
    def test_populate(self, load_mock):
        provider = Ns1Provider('test', 'api-key')

        # Bad auth
        load_mock.side_effect = AuthException('unauthorized')
        zone = Zone('unit.tests.', [])
        with self.assertRaises(AuthException) as ctx:
            provider.populate(zone)
        self.assertEquals(load_mock.side_effect, ctx.exception)

        # General error
        load_mock.reset_mock()
        load_mock.side_effect = ResourceException('boom')
        zone = Zone('unit.tests.', [])
        with self.assertRaises(ResourceException) as ctx:
            provider.populate(zone)
        self.assertEquals(load_mock.side_effect, ctx.exception)
        self.assertEquals(('unit.tests', ), load_mock.call_args[0])

        # Non-existant zone doesn't populate anything
        load_mock.reset_mock()
        load_mock.side_effect = \
            ResourceException('server error: zone not found')
        zone = Zone('unit.tests.', [])
        provider.populate(zone)
        self.assertEquals(set(), zone.records)
        self.assertEquals(('unit.tests', ), load_mock.call_args[0])

        # Existing zone w/o records
        load_mock.reset_mock()
        nsone_zone = DummyZone([])
        load_mock.side_effect = [nsone_zone]
        zone = Zone('unit.tests.', [])
        provider.populate(zone)
        self.assertEquals(set(), zone.records)
        self.assertEquals(('unit.tests', ), load_mock.call_args[0])

        # Existing zone w/records
        load_mock.reset_mock()
        nsone_zone = DummyZone(self.nsone_records)
        load_mock.side_effect = [nsone_zone]
        zone = Zone('unit.tests.', [])
        provider.populate(zone)
        self.assertEquals(self.expected, zone.records)
        self.assertEquals(('unit.tests', ), load_mock.call_args[0])
    def test_sync(self, load_mock, create_mock):
        provider = Ns1Provider('test', 'api-key')

        desired = Zone('unit.tests.', [])
        desired.records.update(self.expected)

        plan = provider.plan(desired)
        # everything except the root NS
        expected_n = len(self.expected) - 1
        self.assertEquals(expected_n, len(plan.changes))

        # Fails, general error
        load_mock.reset_mock()
        create_mock.reset_mock()
        load_mock.side_effect = ResourceException('boom')
        with self.assertRaises(ResourceException) as ctx:
            provider.apply(plan)
        self.assertEquals(load_mock.side_effect, ctx.exception)

        # Fails, bad auth
        load_mock.reset_mock()
        create_mock.reset_mock()
        load_mock.side_effect = \
            ResourceException('server error: zone not found')
        create_mock.side_effect = AuthException('unauthorized')
        with self.assertRaises(AuthException) as ctx:
            provider.apply(plan)
        self.assertEquals(create_mock.side_effect, ctx.exception)

        # non-existant zone, create
        load_mock.reset_mock()
        create_mock.reset_mock()
        load_mock.side_effect = \
            ResourceException('server error: zone not found')
        create_mock.side_effect = None
        got_n = provider.apply(plan)
        self.assertEquals(expected_n, got_n)

        # Update & delete
        load_mock.reset_mock()
        create_mock.reset_mock()
        nsone_zone = DummyZone(self.nsone_records +
                               [{
                                   'type': 'A',
                                   'ttl': 42,
                                   'short_answers': ['9.9.9.9'],
                                   'domain': 'delete-me.unit.tests.',
                               }])
        nsone_zone.data['records'][0]['short_answers'][0] = '2.2.2.2'
        nsone_zone.loadRecord = Mock()
        load_mock.side_effect = [nsone_zone, nsone_zone]
        plan = provider.plan(desired)
        self.assertEquals(2, len(plan.changes))
        self.assertIsInstance(plan.changes[0], Update)
        self.assertIsInstance(plan.changes[1], Delete)

        got_n = provider.apply(plan)
        self.assertEquals(2, got_n)
        nsone_zone.loadRecord.assert_has_calls([
            call('unit.tests', u'A'),
            call().update(answers=[u'1.2.3.4'], ttl=32),
            call('delete-me', u'A'),
            call().delete()
        ])
    def test_populate(self, load_mock):
        provider = Ns1Provider('test', 'api-key')

        # Bad auth
        load_mock.side_effect = AuthException('unauthorized')
        zone = Zone('unit.tests.', [])
        with self.assertRaises(AuthException) as ctx:
            provider.populate(zone)
        self.assertEquals(load_mock.side_effect, ctx.exception)

        # General error
        load_mock.reset_mock()
        load_mock.side_effect = ResourceException('boom')
        zone = Zone('unit.tests.', [])
        with self.assertRaises(ResourceException) as ctx:
            provider.populate(zone)
        self.assertEquals(load_mock.side_effect, ctx.exception)
        self.assertEquals(('unit.tests',), load_mock.call_args[0])

        # Non-existant zone doesn't populate anything
        load_mock.reset_mock()
        load_mock.side_effect = \
            ResourceException('server error: zone not found')
        zone = Zone('unit.tests.', [])
        provider.populate(zone)
        self.assertEquals(set(), zone.records)
        self.assertEquals(('unit.tests',), load_mock.call_args[0])

        # Existing zone w/o records
        load_mock.reset_mock()
        nsone_zone = DummyZone([])
        load_mock.side_effect = [nsone_zone]
        zone_search = Mock()
        zone_search.return_value = [
            {
                "domain": "geo.unit.tests",
                "zone": "unit.tests",
                "type": "A",
                "answers": [
                    {'answer': ['1.1.1.1'], 'meta': {}},
                    {'answer': ['1.2.3.4'],
                     'meta': {'ca_province': ['ON']}},
                    {'answer': ['2.3.4.5'], 'meta': {'us_state': ['NY']}},
                    {'answer': ['3.4.5.6'], 'meta': {'country': ['US']}},
                    {'answer': ['4.5.6.7'],
                     'meta': {'iso_region_code': ['NA-US-WA']}},
                ],
                'ttl': 34,
            },
        ]
        nsone_zone.search = zone_search
        zone = Zone('unit.tests.', [])
        provider.populate(zone)
        self.assertEquals(1, len(zone.records))
        self.assertEquals(('unit.tests',), load_mock.call_args[0])

        # Existing zone w/records
        load_mock.reset_mock()
        nsone_zone = DummyZone(self.nsone_records)
        load_mock.side_effect = [nsone_zone]
        zone_search = Mock()
        zone_search.return_value = [
            {
                "domain": "geo.unit.tests",
                "zone": "unit.tests",
                "type": "A",
                "answers": [
                    {'answer': ['1.1.1.1'], 'meta': {}},
                    {'answer': ['1.2.3.4'],
                     'meta': {'ca_province': ['ON']}},
                    {'answer': ['2.3.4.5'], 'meta': {'us_state': ['NY']}},
                    {'answer': ['3.4.5.6'], 'meta': {'country': ['US']}},
                    {'answer': ['4.5.6.7'],
                     'meta': {'iso_region_code': ['NA-US-WA']}},
                ],
                'ttl': 34,
            },
        ]
        nsone_zone.search = zone_search
        zone = Zone('unit.tests.', [])
        provider.populate(zone)
        self.assertEquals(self.expected, zone.records)
        self.assertEquals(('unit.tests',), load_mock.call_args[0])
    def test_sync(self, load_mock, create_mock):
        provider = Ns1Provider('test', 'api-key')

        desired = Zone('unit.tests.', [])
        for r in self.expected:
            desired.add_record(r)

        plan = provider.plan(desired)
        # everything except the root NS
        expected_n = len(self.expected) - 1
        self.assertEquals(expected_n, len(plan.changes))

        # Fails, general error
        load_mock.reset_mock()
        create_mock.reset_mock()
        load_mock.side_effect = ResourceException('boom')
        with self.assertRaises(ResourceException) as ctx:
            provider.apply(plan)
        self.assertEquals(load_mock.side_effect, ctx.exception)

        # Fails, bad auth
        load_mock.reset_mock()
        create_mock.reset_mock()
        load_mock.side_effect = \
            ResourceException('server error: zone not found')
        create_mock.side_effect = AuthException('unauthorized')
        with self.assertRaises(AuthException) as ctx:
            provider.apply(plan)
        self.assertEquals(create_mock.side_effect, ctx.exception)

        # non-existant zone, create
        load_mock.reset_mock()
        create_mock.reset_mock()
        load_mock.side_effect = \
            ResourceException('server error: zone not found')
        # ugh, need a mock zone with a mock prop since we're using getattr, we
        # can actually control side effects on `meth` with that.
        mock_zone = Mock()
        mock_zone.add_SRV = Mock()
        mock_zone.add_SRV.side_effect = [
            RateLimitException('boo', period=0),
            None,
        ]
        create_mock.side_effect = [mock_zone]
        got_n = provider.apply(plan)
        self.assertEquals(expected_n, got_n)

        # Update & delete
        load_mock.reset_mock()
        create_mock.reset_mock()
        nsone_zone = DummyZone(self.nsone_records + [{
            'type': 'A',
            'ttl': 42,
            'short_answers': ['9.9.9.9'],
            'domain': 'delete-me.unit.tests.',
        }])
        nsone_zone.data['records'][0]['short_answers'][0] = '2.2.2.2'
        nsone_zone.loadRecord = Mock()
        zone_search = Mock()
        zone_search.return_value = [
            {
                "domain": "geo.unit.tests",
                "zone": "unit.tests",
                "type": "A",
                "answers": [
                    {'answer': ['1.1.1.1'], 'meta': {}},
                    {'answer': ['1.2.3.4'],
                     'meta': {'ca_province': ['ON']}},
                    {'answer': ['2.3.4.5'], 'meta': {'us_state': ['NY']}},
                    {'answer': ['3.4.5.6'], 'meta': {'country': ['US']}},
                    {'answer': ['4.5.6.7'],
                     'meta': {'iso_region_code': ['NA-US-WA']}},
                ],
                'ttl': 34,
            },
        ]
        nsone_zone.search = zone_search
        load_mock.side_effect = [nsone_zone, nsone_zone]
        plan = provider.plan(desired)
        self.assertEquals(3, len(plan.changes))
        self.assertIsInstance(plan.changes[0], Update)
        self.assertIsInstance(plan.changes[2], Delete)
        # ugh, we need a mock record that can be returned from loadRecord for
        # the update and delete targets, we can add our side effects to that to
        # trigger rate limit handling
        mock_record = Mock()
        mock_record.update.side_effect = [
            RateLimitException('one', period=0),
            None,
            None,
        ]
        mock_record.delete.side_effect = [
            RateLimitException('two', period=0),
            None,
            None,
        ]
        nsone_zone.loadRecord.side_effect = [mock_record, mock_record,
                                             mock_record]
        got_n = provider.apply(plan)
        self.assertEquals(3, got_n)
        nsone_zone.loadRecord.assert_has_calls([
            call('unit.tests', u'A'),
            call('geo', u'A'),
            call('delete-me', u'A'),
        ])
        mock_record.assert_has_calls([
            call.update(answers=[{'answer': [u'1.2.3.4'], 'meta': {}}],
                        filters=[],
                        ttl=32),
            call.update(answers=[{u'answer': [u'1.2.3.4'], u'meta': {}}],
                        filters=[],
                        ttl=32),
            call.update(
                answers=[
                    {u'answer': [u'101.102.103.104'], u'meta': {}},
                    {u'answer': [u'101.102.103.105'], u'meta': {}},
                    {
                        u'answer': [u'201.202.203.204'],
                        u'meta': {
                            u'iso_region_code': [u'NA-US-NY']
                        },
                    },
                ],
                filters=[
                    {u'filter': u'shuffle', u'config': {}},
                    {u'filter': u'geotarget_country', u'config': {}},
                    {u'filter': u'select_first_n', u'config': {u'N': 1}},
                ],
                ttl=34),
            call.delete(),
            call.delete()
        ])
Beispiel #7
0
    def test_sync(self, load_mock, create_mock):
        provider = Ns1Provider('test', 'api-key')

        desired = Zone('unit.tests.', [])
        for r in self.expected:
            desired.add_record(r)

        plan = provider.plan(desired)
        # everything except the root NS
        expected_n = len(self.expected) - 1
        self.assertEquals(expected_n, len(plan.changes))

        # Fails, general error
        load_mock.reset_mock()
        create_mock.reset_mock()
        load_mock.side_effect = ResourceException('boom')
        with self.assertRaises(ResourceException) as ctx:
            provider.apply(plan)
        self.assertEquals(load_mock.side_effect, ctx.exception)

        # Fails, bad auth
        load_mock.reset_mock()
        create_mock.reset_mock()
        load_mock.side_effect = \
            ResourceException('server error: zone not found')
        create_mock.side_effect = AuthException('unauthorized')
        with self.assertRaises(AuthException) as ctx:
            provider.apply(plan)
        self.assertEquals(create_mock.side_effect, ctx.exception)

        # non-existant zone, create
        load_mock.reset_mock()
        create_mock.reset_mock()
        load_mock.side_effect = \
            ResourceException('server error: zone not found')
        # ugh, need a mock zone with a mock prop since we're using getattr, we
        # can actually control side effects on `meth` with that.
        mock_zone = Mock()
        mock_zone.add_SRV = Mock()
        mock_zone.add_SRV.side_effect = [
            RateLimitException('boo', period=0),
            None,
        ]
        create_mock.side_effect = [mock_zone]
        got_n = provider.apply(plan)
        self.assertEquals(expected_n, got_n)

        # Update & delete
        load_mock.reset_mock()
        create_mock.reset_mock()
        nsone_zone = DummyZone(self.nsone_records +
                               [{
                                   'type': 'A',
                                   'ttl': 42,
                                   'short_answers': ['9.9.9.9'],
                                   'domain': 'delete-me.unit.tests.',
                               }])
        nsone_zone.data['records'][0]['short_answers'][0] = '2.2.2.2'
        nsone_zone.loadRecord = Mock()
        load_mock.side_effect = [nsone_zone, nsone_zone]
        plan = provider.plan(desired)
        self.assertEquals(2, len(plan.changes))
        self.assertIsInstance(plan.changes[0], Update)
        self.assertIsInstance(plan.changes[1], Delete)
        # ugh, we need a mock record that can be returned from loadRecord for
        # the update and delete targets, we can add our side effects to that to
        # trigger rate limit handling
        mock_record = Mock()
        mock_record.update.side_effect = [
            RateLimitException('one', period=0),
            None,
        ]
        mock_record.delete.side_effect = [
            RateLimitException('two', period=0),
            None,
        ]
        nsone_zone.loadRecord.side_effect = [mock_record, mock_record]
        got_n = provider.apply(plan)
        self.assertEquals(2, got_n)
        nsone_zone.loadRecord.assert_has_calls([
            call('unit.tests', u'A'),
            call('delete-me', u'A'),
        ])
        mock_record.assert_has_calls(
            [call.update(answers=[u'1.2.3.4'], ttl=32),
             call.delete()])
    def test_sync(self, zone_retrieve_mock, zone_create_mock,
                  record_retrieve_mock, record_create_mock, record_update_mock,
                  record_delete_mock):
        provider = Ns1Provider('test', 'api-key')

        desired = Zone('unit.tests.', [])
        for r in self.expected:
            desired.add_record(r)

        plan = provider.plan(desired)
        # everything except the root NS
        expected_n = len(self.expected) - 1
        self.assertEquals(expected_n, len(plan.changes))
        self.assertTrue(plan.exists)

        # Fails, general error
        zone_retrieve_mock.reset_mock()
        zone_create_mock.reset_mock()
        zone_retrieve_mock.side_effect = ResourceException('boom')
        with self.assertRaises(ResourceException) as ctx:
            provider.apply(plan)
        self.assertEquals(zone_retrieve_mock.side_effect, ctx.exception)

        # Fails, bad auth
        zone_retrieve_mock.reset_mock()
        zone_create_mock.reset_mock()
        zone_retrieve_mock.side_effect = \
            ResourceException('server error: zone not found')
        zone_create_mock.side_effect = AuthException('unauthorized')
        with self.assertRaises(AuthException) as ctx:
            provider.apply(plan)
        self.assertEquals(zone_create_mock.side_effect, ctx.exception)

        # non-existent zone, create
        zone_retrieve_mock.reset_mock()
        zone_create_mock.reset_mock()
        zone_retrieve_mock.side_effect = \
            ResourceException('server error: zone not found')

        zone_create_mock.side_effect = ['foo']
        # Test out the create rate-limit handling, then 9 successes
        record_create_mock.side_effect = [
            RateLimitException('boo', period=0),
        ] + ([None] * 9)

        got_n = provider.apply(plan)
        self.assertEquals(expected_n, got_n)

        # Zone was created
        zone_create_mock.assert_has_calls([call('unit.tests')])
        # Checking that we got some of the expected records too
        record_create_mock.assert_has_calls([
            call('unit.tests',
                 'unit.tests',
                 'A',
                 answers=[{
                     'answer': ['1.2.3.4'],
                     'meta': {}
                 }],
                 filters=[],
                 ttl=32),
            call('unit.tests',
                 'unit.tests',
                 'CAA',
                 answers=[(0, 'issue', 'ca.unit.tests')],
                 ttl=40),
            call('unit.tests',
                 'unit.tests',
                 'MX',
                 answers=[(10, 'mx1.unit.tests.'), (20, 'mx2.unit.tests.')],
                 ttl=35),
        ])

        # Update & delete
        zone_retrieve_mock.reset_mock()
        zone_create_mock.reset_mock()

        ns1_zone = {
            'records':
            self.ns1_records + [
                {
                    'type': 'A',
                    'ttl': 42,
                    'short_answers': ['9.9.9.9'],
                    'domain': 'delete-me.unit.tests.',
                },
                {
                    "domain":
                    "geo.unit.tests",
                    "zone":
                    "unit.tests",
                    "type":
                    "A",
                    "short_answers": [
                        '1.1.1.1',
                        '1.2.3.4',
                        '2.3.4.5',
                        '3.4.5.6',
                        '4.5.6.7',
                    ],
                    'tier':
                    3,  # This flags it as advacned, full load required
                    'ttl':
                    34,
                }
            ],
        }
        ns1_zone['records'][0]['short_answers'][0] = '2.2.2.2'

        record_retrieve_mock.side_effect = [{
            "domain":
            "geo.unit.tests",
            "zone":
            "unit.tests",
            "type":
            "A",
            "answers": [
                {
                    'answer': ['1.1.1.1'],
                    'meta': {}
                },
                {
                    'answer': ['1.2.3.4'],
                    'meta': {
                        'ca_province': ['ON']
                    }
                },
                {
                    'answer': ['2.3.4.5'],
                    'meta': {
                        'us_state': ['NY']
                    }
                },
                {
                    'answer': ['3.4.5.6'],
                    'meta': {
                        'country': ['US']
                    }
                },
                {
                    'answer': ['4.5.6.7'],
                    'meta': {
                        'iso_region_code': ['NA-US-WA']
                    }
                },
            ],
            'tier':
            3,
            'ttl':
            34,
        }]

        zone_retrieve_mock.side_effect = [ns1_zone, ns1_zone]
        plan = provider.plan(desired)
        self.assertEquals(3, len(plan.changes))
        # Shouldn't rely on order so just count classes
        classes = defaultdict(lambda: 0)
        for change in plan.changes:
            classes[change.__class__] += 1
        self.assertEquals(1, classes[Delete])
        self.assertEquals(2, classes[Update])

        record_update_mock.side_effect = [
            RateLimitException('one', period=0),
            None,
            None,
        ]
        record_delete_mock.side_effect = [
            RateLimitException('two', period=0),
            None,
            None,
        ]

        got_n = provider.apply(plan)
        self.assertEquals(3, got_n)

        record_update_mock.assert_has_calls([
            call('unit.tests',
                 'unit.tests',
                 'A',
                 answers=[{
                     'answer': ['1.2.3.4'],
                     'meta': {}
                 }],
                 filters=[],
                 ttl=32),
            call('unit.tests',
                 'unit.tests',
                 'A',
                 answers=[{
                     'answer': ['1.2.3.4'],
                     'meta': {}
                 }],
                 filters=[],
                 ttl=32),
            call('unit.tests',
                 'geo.unit.tests',
                 'A',
                 answers=[{
                     'answer': ['101.102.103.104'],
                     'meta': {}
                 }, {
                     'answer': ['101.102.103.105'],
                     'meta': {}
                 }, {
                     'answer': ['201.202.203.204'],
                     'meta': {
                         'iso_region_code': ['NA-US-NY']
                     }
                 }],
                 filters=[{
                     'filter': 'shuffle',
                     'config': {}
                 }, {
                     'filter': 'geotarget_country',
                     'config': {}
                 }, {
                     'filter': 'select_first_n',
                     'config': {
                         'N': 1
                     }
                 }],
                 ttl=34)
        ])
    def test_populate(self, zone_retrieve_mock):
        provider = Ns1Provider('test', 'api-key')

        # Bad auth
        zone_retrieve_mock.side_effect = AuthException('unauthorized')
        zone = Zone('unit.tests.', [])
        with self.assertRaises(AuthException) as ctx:
            provider.populate(zone)
        self.assertEquals(zone_retrieve_mock.side_effect, ctx.exception)

        # General error
        zone_retrieve_mock.reset_mock()
        zone_retrieve_mock.side_effect = ResourceException('boom')
        zone = Zone('unit.tests.', [])
        with self.assertRaises(ResourceException) as ctx:
            provider.populate(zone)
        self.assertEquals(zone_retrieve_mock.side_effect, ctx.exception)
        self.assertEquals(('unit.tests', ), zone_retrieve_mock.call_args[0])

        # Non-existent zone doesn't populate anything
        zone_retrieve_mock.reset_mock()
        zone_retrieve_mock.side_effect = \
            ResourceException('server error: zone not found')
        zone = Zone('unit.tests.', [])
        exists = provider.populate(zone)
        self.assertEquals(set(), zone.records)
        self.assertEquals(('unit.tests', ), zone_retrieve_mock.call_args[0])
        self.assertFalse(exists)

        # Existing zone w/o records
        zone_retrieve_mock.reset_mock()
        ns1_zone = {
            'records': [{
                "domain":
                "geo.unit.tests",
                "zone":
                "unit.tests",
                "type":
                "A",
                "answers": [
                    {
                        'answer': ['1.1.1.1'],
                        'meta': {}
                    },
                    {
                        'answer': ['1.2.3.4'],
                        'meta': {
                            'ca_province': ['ON']
                        }
                    },
                    {
                        'answer': ['2.3.4.5'],
                        'meta': {
                            'us_state': ['NY']
                        }
                    },
                    {
                        'answer': ['3.4.5.6'],
                        'meta': {
                            'country': ['US']
                        }
                    },
                    {
                        'answer': ['4.5.6.7'],
                        'meta': {
                            'iso_region_code': ['NA-US-WA']
                        }
                    },
                ],
                'ttl':
                34,
            }],
        }
        zone_retrieve_mock.side_effect = [ns1_zone]
        zone = Zone('unit.tests.', [])
        provider.populate(zone)
        self.assertEquals(1, len(zone.records))
        self.assertEquals(('unit.tests', ), zone_retrieve_mock.call_args[0])

        # Existing zone w/records
        zone_retrieve_mock.reset_mock()
        ns1_zone = {
            'records':
            self.ns1_records + [{
                "domain":
                "geo.unit.tests",
                "zone":
                "unit.tests",
                "type":
                "A",
                "answers": [
                    {
                        'answer': ['1.1.1.1'],
                        'meta': {}
                    },
                    {
                        'answer': ['1.2.3.4'],
                        'meta': {
                            'ca_province': ['ON']
                        }
                    },
                    {
                        'answer': ['2.3.4.5'],
                        'meta': {
                            'us_state': ['NY']
                        }
                    },
                    {
                        'answer': ['3.4.5.6'],
                        'meta': {
                            'country': ['US']
                        }
                    },
                    {
                        'answer': ['4.5.6.7'],
                        'meta': {
                            'iso_region_code': ['NA-US-WA']
                        }
                    },
                ],
                'ttl':
                34,
            }],
        }
        zone_retrieve_mock.side_effect = [ns1_zone]
        zone = Zone('unit.tests.', [])
        provider.populate(zone)
        self.assertEquals(self.expected, zone.records)
        self.assertEquals(('unit.tests', ), zone_retrieve_mock.call_args[0])

        # Test skipping unsupported record type
        zone_retrieve_mock.reset_mock()
        ns1_zone = {
            'records':
            self.ns1_records + [{
                'type': 'UNSUPPORTED',
                'ttl': 42,
                'short_answers': ['unsupported'],
                'domain': 'unsupported.unit.tests.',
            }, {
                "domain":
                "geo.unit.tests",
                "zone":
                "unit.tests",
                "type":
                "A",
                "answers": [
                    {
                        'answer': ['1.1.1.1'],
                        'meta': {}
                    },
                    {
                        'answer': ['1.2.3.4'],
                        'meta': {
                            'ca_province': ['ON']
                        }
                    },
                    {
                        'answer': ['2.3.4.5'],
                        'meta': {
                            'us_state': ['NY']
                        }
                    },
                    {
                        'answer': ['3.4.5.6'],
                        'meta': {
                            'country': ['US']
                        }
                    },
                    {
                        'answer': ['4.5.6.7'],
                        'meta': {
                            'iso_region_code': ['NA-US-WA']
                        }
                    },
                ],
                'ttl':
                34,
            }],
        }
        zone_retrieve_mock.side_effect = [ns1_zone]
        zone = Zone('unit.tests.', [])
        provider.populate(zone)
        self.assertEquals(self.expected, zone.records)
        self.assertEquals(('unit.tests', ), zone_retrieve_mock.call_args[0])
Beispiel #10
0
    def test_populate(self):
        provider = Ns1Provider('test', 'api-key')
        unit_test_zone = octodns_test_zone()

        # General errors (bad request, forbidden, not found, internal, timeout)
        for code in [400, 403, 404, 500, 504]:
            with requests_mock() as mock:
                mock.get(ANY, status_code=code)
                with self.assertRaises(ResourceException):
                    provider.populate(Zone('unit.tests.', []))

        # Auth error
        with requests_mock() as mock:
            mock.get(ANY, status_code=401)
            with self.assertRaises(AuthException):
                provider.populate(Zone('unit.tests.', []))

        # Zone not found, exception handled internally
        with requests_mock() as mock:
            mock.get(endpoint('/zones/unit.tests'),
                     status_code=404,
                     json={"message": "zone not found"})
            provider.populate(Zone('unit.tests.', []))

        # Ratelimit error
        with requests_mock() as mock:
            # First return a 429 (rate limit error) and then a 200 to check that
            # RateLimitExceptions are handled internally
            mock.get(ANY, [{
                'status_code': 429,
                'json': {}
            }, {
                'status_code': 200,
                'json': {}
            }])
            provider.populate(Zone('unit.tests.', []))
            provider._reset_cache()

        # Existing NS1 zone with no records
        with requests_mock() as mock:
            mock.get(endpoint('/zones/unit.tests'),
                     json={
                         'zone': 'unit.tests',
                         'records': [],
                     })
            zone = Zone('unit.tests.', [])
            self.assertTrue(provider.populate(zone))
            self.assertEquals(set(), zone.records)
            provider._reset_cache()

        # Test skipping unsupported record types
        with requests_mock() as mock:
            mock.get(endpoint('/zones/unit.tests'),
                     json={
                         'zone':
                         'unit.tests',
                         'records': [
                             {
                                 'domain': 'unsupported.unit.tests',
                                 'type': 'UNSUPPORTED',
                                 'ttl': 30,
                                 'short_answers': ['1.1.1.1'],
                                 'tier': 1,
                             },
                         ],
                     })
            zone = Zone('unit.tests.', [])
            self.assertTrue(provider.populate(zone))
            self.assertEquals(set(), zone.records)
            provider._reset_cache()

        # Test populating full unit.tests zone
        with requests_mock() as mock:
            mock.get(endpoint('/zones/unit.tests'), json=self.ns1_zone)
            mock.get(endpoint('/zones/unit.tests/unit.tests/A'),
                     json=self.ns1_geo_record)
            zone = Zone('unit.tests.', [])
            self.assertTrue(provider.populate(zone))
            self.assertEquals(15, len(zone.records))
            self.assertEquals(0, len(unit_test_zone.changes(zone, provider)))
            # Note: intentionally don't reset cache here

        # Test that repopulating the same zone just pulls from cache and makes
        # no actual calls.
        with requests_mock() as mock:
            mock.get(ANY, status_code=500)
            zone = Zone('unit.tests.', [])
            self.assertTrue(provider.populate(zone))
            self.assertEquals(15, len(zone.records))
            self.assertEquals(0, len(unit_test_zone.changes(zone, provider)))
            provider._reset_cache()
Beispiel #11
0
    def test_apply(self):
        provider = Ns1Provider('test', 'api-key')
        unit_test_zone = octodns_test_zone()

        zones_mock = MagicMock(return_value=None)
        zones_mock.retrieve.return_value = self.ns1_zone
        provider._NS1Zones = zones_mock

        records_mock = MagicMock(return_value=None)
        records_mock.retrieve.return_value = self.ns1_geo_record
        provider._NS1Records = records_mock

        # Test creating the unit.tests zones from scratch.
        provider._zone_cache['unit.tests'] = None
        plan = provider.plan(unit_test_zone)
        provider._reset_cache()

        self.assertFalse(plan.exists)
        self.assertEquals(14, len(plan.changes))
        self.assertEquals(14, provider.apply(plan))
        self.assertEquals(0, zones_mock.retrieve.call_count)
        self.assertEquals(1, zones_mock.create.call_count)
        self.assertEquals(0, records_mock.retrieve.call_count)
        self.assertEquals(14, records_mock.create.call_count)
        self.assertEquals(0, records_mock.update.call_count)

        # Reset everything.
        provider._reset_cache()
        zones_mock.reset_mock()
        records_mock.reset_mock()
        unit_test_zone = octodns_test_zone()

        # Test adding a record.
        unit_test_zone.add_record(
            Record.new(unit_test_zone, 'ns1', {
                'ttl': 3600,
                'type': 'A',
                'values': ['3.3.3.3']
            }))
        plan = provider.plan(unit_test_zone)

        self.assertTrue(plan.exists)
        self.assertEquals(1, len(plan.changes))
        self.assertEquals(1, provider.apply(plan))
        self.assertEquals(1, zones_mock.retrieve.call_count)
        self.assertEquals(0, zones_mock.create.call_count)
        self.assertEquals(1, records_mock.retrieve.call_count)
        self.assertEquals(1, records_mock.create.call_count)
        self.assertEquals(0, records_mock.update.call_count)
        self.assertEquals(0, records_mock.delete.call_count)

        # Reset everything.
        provider._reset_cache()
        zones_mock.reset_mock()
        records_mock.reset_mock()
        unit_test_zone = octodns_test_zone()

        # Test changing a record.
        remove_octodns_record(unit_test_zone, '', 'A')
        rec = deepcopy(self.ns1_geo_record)
        rec['answers'].append({
            "answer": ["100.100.100.100"],
            "meta": {
                "note": "octodns_region_code:NS-CA-BC",
                "ca_province": ["BC"]
            }
        })
        args = provider._data_for('A', rec)
        unit_test_zone.add_record(Record.new(unit_test_zone, '', args))

        plan = provider.plan(unit_test_zone)
        self.assertTrue(plan.exists)
        self.assertEquals(1, len(plan.changes))
        self.assertEquals(1, provider.apply(plan))
        self.assertEquals(1, zones_mock.retrieve.call_count)
        self.assertEquals(0, zones_mock.create.call_count)
        self.assertEquals(1, records_mock.retrieve.call_count)
        self.assertEquals(0, records_mock.create.call_count)
        self.assertEquals(1, records_mock.update.call_count)
        self.assertEquals(0, records_mock.delete.call_count)

        # Reset everything.
        provider._reset_cache()
        zones_mock.reset_mock()
        records_mock.reset_mock()
        unit_test_zone = octodns_test_zone()

        # Test removing a record.
        zones_mock.retrieve.return_value = self.ns1_zone
        records_mock.retrieve.return_value = self.ns1_geo_record

        remove_octodns_record(unit_test_zone, 'cname', 'CNAME')
        plan = provider.plan(unit_test_zone)
        self.assertTrue(plan.exists)
        self.assertEquals(1, len(plan.changes))
        self.assertEquals(1, provider.apply(plan))
        self.assertEquals(1, zones_mock.retrieve.call_count)
        self.assertEquals(0, zones_mock.create.call_count)
        self.assertEquals(1, records_mock.retrieve.call_count)
        self.assertEquals(0, records_mock.create.call_count)
        self.assertEquals(0, records_mock.update.call_count)
        self.assertEquals(1, records_mock.delete.call_count)