mirror of
				https://github.com/github/octodns.git
				synced 2024-05-11 05:55:00 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			293 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			293 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| #
 | |
| #
 | |
| #
 | |
| 
 | |
| from __future__ import absolute_import, division, print_function, \
 | |
|     unicode_literals
 | |
| 
 | |
| from json import loads, dumps
 | |
| from os.path import dirname, join
 | |
| from requests import HTTPError
 | |
| from requests_mock import ANY, mock as requests_mock
 | |
| from unittest import TestCase
 | |
| 
 | |
| from octodns.record import Record
 | |
| from octodns.provider.powerdns import PowerDnsProvider
 | |
| from octodns.provider.yaml import YamlProvider
 | |
| from octodns.zone import Zone
 | |
| 
 | |
| EMPTY_TEXT = '''
 | |
| {
 | |
|     "account": "",
 | |
|     "dnssec": false,
 | |
|     "id": "xunit.tests.",
 | |
|     "kind": "Master",
 | |
|     "last_check": 0,
 | |
|     "masters": [],
 | |
|     "name": "xunit.tests.",
 | |
|     "notified_serial": 0,
 | |
|     "rrsets": [],
 | |
|     "serial": 2017012801,
 | |
|     "soa_edit": "",
 | |
|     "soa_edit_api": "INCEPTION-INCREMENT",
 | |
|     "url": "api/v1/servers/localhost/zones/xunit.tests."
 | |
| }
 | |
| '''
 | |
| 
 | |
| with open('./tests/fixtures/powerdns-full-data.json') as fh:
 | |
|     FULL_TEXT = fh.read()
 | |
| 
 | |
| 
 | |
| class TestPowerDnsProvider(TestCase):
 | |
| 
 | |
|     def test_provider(self):
 | |
|         provider = PowerDnsProvider('test', 'non.existant', 'api-key',
 | |
|                                     nameserver_values=['8.8.8.8.',
 | |
|                                                        '9.9.9.9.'])
 | |
| 
 | |
|         # Bad auth
 | |
|         with requests_mock() as mock:
 | |
|             mock.get(ANY, status_code=401, text='Unauthorized')
 | |
| 
 | |
|             with self.assertRaises(Exception) as ctx:
 | |
|                 zone = Zone('unit.tests.', [])
 | |
|                 provider.populate(zone)
 | |
|             self.assertTrue('unauthorized' in ctx.exception.message)
 | |
| 
 | |
|         # General error
 | |
|         with requests_mock() as mock:
 | |
|             mock.get(ANY, status_code=502, text='Things caught fire')
 | |
| 
 | |
|             with self.assertRaises(HTTPError) as ctx:
 | |
|                 zone = Zone('unit.tests.', [])
 | |
|                 provider.populate(zone)
 | |
|             self.assertEquals(502, ctx.exception.response.status_code)
 | |
| 
 | |
|         # Non-existant zone doesn't populate anything
 | |
|         with requests_mock() as mock:
 | |
|             mock.get(ANY, status_code=422,
 | |
|                      json={'error': "Could not find domain 'unit.tests.'"})
 | |
| 
 | |
|             zone = Zone('unit.tests.', [])
 | |
|             provider.populate(zone)
 | |
|             self.assertEquals(set(), zone.records)
 | |
| 
 | |
|         # The rest of this is messy/complicated b/c it's dealing with mocking
 | |
| 
 | |
|         expected = Zone('unit.tests.', [])
 | |
|         source = YamlProvider('test', join(dirname(__file__), 'config'))
 | |
|         source.populate(expected)
 | |
|         expected_n = len(expected.records) - 2
 | |
|         self.assertEquals(16, expected_n)
 | |
| 
 | |
|         # No diffs == no changes
 | |
|         with requests_mock() as mock:
 | |
|             mock.get(ANY, status_code=200, text=FULL_TEXT)
 | |
| 
 | |
|             zone = Zone('unit.tests.', [])
 | |
|             provider.populate(zone)
 | |
|             self.assertEquals(16, len(zone.records))
 | |
|             changes = expected.changes(zone, provider)
 | |
|             self.assertEquals(0, len(changes))
 | |
| 
 | |
|         # Used in a minute
 | |
|         def assert_rrsets_callback(request, context):
 | |
|             data = loads(request.body)
 | |
|             self.assertEquals(expected_n, len(data['rrsets']))
 | |
|             return ''
 | |
| 
 | |
|         # No existing records -> creates for every record in expected
 | |
|         with requests_mock() as mock:
 | |
|             mock.get(ANY, status_code=200, text=EMPTY_TEXT)
 | |
|             # post 201, is response to the create with data
 | |
|             mock.patch(ANY, status_code=201, text=assert_rrsets_callback)
 | |
| 
 | |
|             plan = provider.plan(expected)
 | |
|             self.assertEquals(expected_n, len(plan.changes))
 | |
|             self.assertEquals(expected_n, provider.apply(plan))
 | |
|             self.assertTrue(plan.exists)
 | |
| 
 | |
|         # Non-existent zone -> creates for every record in expected
 | |
|         # OMG this is fucking ugly, probably better to ditch requests_mocks and
 | |
|         # just mock things for real as it doesn't seem to provide a way to get
 | |
|         # at the request params or verify that things were called from what I
 | |
|         # can tell
 | |
|         not_found = {'error': "Could not find domain 'unit.tests.'"}
 | |
|         with requests_mock() as mock:
 | |
|             # get 422's, unknown zone
 | |
|             mock.get(ANY, status_code=422, text='')
 | |
|             # patch 422's, unknown zone
 | |
|             mock.patch(ANY, status_code=422, text=dumps(not_found))
 | |
|             # post 201, is response to the create with data
 | |
|             mock.post(ANY, status_code=201, text=assert_rrsets_callback)
 | |
| 
 | |
|             plan = provider.plan(expected)
 | |
|             self.assertEquals(expected_n, len(plan.changes))
 | |
|             self.assertEquals(expected_n, provider.apply(plan))
 | |
|             self.assertFalse(plan.exists)
 | |
| 
 | |
|         with requests_mock() as mock:
 | |
|             # get 422's, unknown zone
 | |
|             mock.get(ANY, status_code=422, text='')
 | |
|             # patch 422's,
 | |
|             data = {'error': "Key 'name' not present or not a String"}
 | |
|             mock.patch(ANY, status_code=422, text=dumps(data))
 | |
| 
 | |
|             with self.assertRaises(HTTPError) as ctx:
 | |
|                 plan = provider.plan(expected)
 | |
|                 provider.apply(plan)
 | |
|             response = ctx.exception.response
 | |
|             self.assertEquals(422, response.status_code)
 | |
|             self.assertTrue('error' in response.json())
 | |
| 
 | |
|         with requests_mock() as mock:
 | |
|             # get 422's, unknown zone
 | |
|             mock.get(ANY, status_code=422, text='')
 | |
|             # patch 500's, things just blew up
 | |
|             mock.patch(ANY, status_code=500, text='')
 | |
| 
 | |
|             with self.assertRaises(HTTPError):
 | |
|                 plan = provider.plan(expected)
 | |
|                 provider.apply(plan)
 | |
| 
 | |
|         with requests_mock() as mock:
 | |
|             # get 422's, unknown zone
 | |
|             mock.get(ANY, status_code=422, text='')
 | |
|             # patch 500's, things just blew up
 | |
|             mock.patch(ANY, status_code=422, text=dumps(not_found))
 | |
|             # post 422's, something wrong with create
 | |
|             mock.post(ANY, status_code=422, text='Hello Word!')
 | |
| 
 | |
|             with self.assertRaises(HTTPError):
 | |
|                 plan = provider.plan(expected)
 | |
|                 provider.apply(plan)
 | |
| 
 | |
|     def test_small_change(self):
 | |
|         provider = PowerDnsProvider('test', 'non.existant', 'api-key')
 | |
| 
 | |
|         expected = Zone('unit.tests.', [])
 | |
|         source = YamlProvider('test', join(dirname(__file__), 'config'))
 | |
|         source.populate(expected)
 | |
|         self.assertEquals(18, len(expected.records))
 | |
| 
 | |
|         # A small change to a single record
 | |
|         with requests_mock() as mock:
 | |
|             mock.get(ANY, status_code=200, text=FULL_TEXT)
 | |
| 
 | |
|             missing = Zone(expected.name, [])
 | |
|             # Find and delete the SPF record
 | |
|             for record in expected.records:
 | |
|                 if record._type != 'SPF':
 | |
|                     missing.add_record(record)
 | |
| 
 | |
|             def assert_delete_callback(request, context):
 | |
|                 self.assertEquals({
 | |
|                     'rrsets': [{
 | |
|                         'records': [
 | |
|                             {'content': '"v=spf1 ip4:192.168.0.1/16-all"',
 | |
|                              'disabled': False}
 | |
|                         ],
 | |
|                         'changetype': 'DELETE',
 | |
|                         'type': 'SPF',
 | |
|                         'name': 'spf.unit.tests.',
 | |
|                         'ttl': 600
 | |
|                     }]
 | |
|                 }, loads(request.body))
 | |
|                 return ''
 | |
| 
 | |
|             mock.patch(ANY, status_code=201, text=assert_delete_callback)
 | |
| 
 | |
|             plan = provider.plan(missing)
 | |
|             self.assertEquals(1, len(plan.changes))
 | |
|             self.assertEquals(1, provider.apply(plan))
 | |
| 
 | |
|     def test_existing_nameservers(self):
 | |
|         ns_values = ['8.8.8.8.', '9.9.9.9.']
 | |
|         provider = PowerDnsProvider('test', 'non.existant', 'api-key',
 | |
|                                     nameserver_values=ns_values)
 | |
| 
 | |
|         expected = Zone('unit.tests.', [])
 | |
|         ns_record = Record.new(expected, '', {
 | |
|             'type': 'NS',
 | |
|             'ttl': 600,
 | |
|             'values': ns_values
 | |
|         })
 | |
|         expected.add_record(ns_record)
 | |
| 
 | |
|         # no changes
 | |
|         with requests_mock() as mock:
 | |
|             data = {
 | |
|                 'rrsets': [{
 | |
|                     'comments': [],
 | |
|                     'name': 'unit.tests.',
 | |
|                     'records': [
 | |
|                         {
 | |
|                             'content': '8.8.8.8.',
 | |
|                             'disabled': False
 | |
|                         },
 | |
|                         {
 | |
|                             'content': '9.9.9.9.',
 | |
|                             'disabled': False
 | |
|                         }
 | |
|                     ],
 | |
|                     'ttl': 600,
 | |
|                     'type': 'NS'
 | |
|                 }, {
 | |
|                     'comments': [],
 | |
|                     'name': 'unit.tests.',
 | |
|                     'records': [{
 | |
|                         'content': '1.2.3.4',
 | |
|                         'disabled': False,
 | |
|                     }],
 | |
|                     'ttl': 60,
 | |
|                     'type': 'A'
 | |
|                 }]
 | |
|             }
 | |
|             mock.get(ANY, status_code=200, json=data)
 | |
| 
 | |
|             unrelated_record = Record.new(expected, '', {
 | |
|                 'type': 'A',
 | |
|                 'ttl': 60,
 | |
|                 'value': '1.2.3.4'
 | |
|             })
 | |
|             expected.add_record(unrelated_record)
 | |
|             plan = provider.plan(expected)
 | |
|             self.assertFalse(plan)
 | |
|             # remove it now that we don't need the unrelated change any longer
 | |
|             expected._remove_record(unrelated_record)
 | |
| 
 | |
|         # ttl diff
 | |
|         with requests_mock() as mock:
 | |
|             data = {
 | |
|                 'rrsets': [{
 | |
|                     'comments': [],
 | |
|                     'name': 'unit.tests.',
 | |
|                     'records': [
 | |
|                         {
 | |
|                             'content': '8.8.8.8.',
 | |
|                             'disabled': False
 | |
|                         },
 | |
|                         {
 | |
|                             'content': '9.9.9.9.',
 | |
|                             'disabled': False
 | |
|                         },
 | |
|                     ],
 | |
|                     'ttl': 3600,
 | |
|                     'type': 'NS'
 | |
|                 }]
 | |
|             }
 | |
|             mock.get(ANY, status_code=200, json=data)
 | |
| 
 | |
|             plan = provider.plan(expected)
 | |
|             self.assertEquals(1, len(plan.changes))
 | |
| 
 | |
|         # create
 | |
|         with requests_mock() as mock:
 | |
|             data = {
 | |
|                 'rrsets': []
 | |
|             }
 | |
|             mock.get(ANY, status_code=200, json=data)
 | |
| 
 | |
|             plan = provider.plan(expected)
 | |
|             self.assertEquals(1, len(plan.changes))
 |