def test_not_exist_domain(self, fake_http): fake_http.get(f'{self.API_URL}/', status_code=404, json='') fake_http.head(f'{self.API_URL}/', headers={'X-Total-Count': str(len(self.domain))}) fake_http.post(f'{self.API_URL}/', json={ "name": "unit.tests", "create_date": 1507154178, "id": 100000 }) fake_http.get(f'{self.API_URL}/unit.tests/records/', json=list()) fake_http.head(f'{self.API_URL}/unit.tests/records/', headers={'X-Total-Count': str(len(self.api_record))}) fake_http.post(f'{self.API_URL}/100000/records/', json=list()) provider = SelectelProvider(123, 'test_token') zone = Zone('unit.tests.', []) for record in self.expected: zone.add_record(record) plan = provider.plan(zone) self.assertEquals(8, len(plan.changes)) self.assertEquals(8, provider.apply(plan))
def test_plan_with_unsupported_type(self): zone = Zone('unit.tests.', []) # supported supported = Record.new(zone, 'a', { 'ttl': 30, 'type': 'A', 'value': '1.2.3.4' }) zone.add_record(supported) # not supported not_supported = Record.new( zone, 'aaaa', { 'ttl': 30, 'type': 'AAAA', 'value': '2601:644:500:e210:62f8:1dff:feb8:947a', }, ) zone.add_record(not_supported) provider = HelperProvider() plan = provider.plan(zone) self.assertTrue(plan) self.assertEqual(1, len(plan.changes)) self.assertEqual(supported, plan.changes[0].new)
def test_populate_in_addr_arpa(self): got = Zone('3.2.10.in-addr.arpa.', []) self.source.populate(got) expected = Zone('3.2.10.in-addr.arpa.', []) for name, data in ( ('10', { 'type': 'PTR', 'ttl': 3600, 'value': 'a-ptr.example.com.' }), ('11', { 'type': 'PTR', 'ttl': 30, 'value': 'a-ptr-2.example.com.' }), ('8', { 'type': 'PTR', 'ttl': 3600, 'value': 'has-dup-def123.example.com.' }), ('7', { 'type': 'PTR', 'ttl': 1800, 'value': 'some-host-abc123.example.com.' }), ): record = Record.new(expected, name, data) expected.add_record(record) changes = expected.changes(got, SimpleProvider()) self.assertEqual([], changes)
def test_extra_change_no_health_check(self): provider, stubber = self._get_stubbed_provider() list_hosted_zones_resp = { 'HostedZones': [{ 'Name': 'unit.tests.', 'Id': 'z42', 'CallerReference': 'abc', }], 'Marker': 'm', 'IsTruncated': False, 'MaxItems': '100', } stubber.add_response('list_hosted_zones', list_hosted_zones_resp, {}) # record with geo and no health check returns change desired = Zone('unit.tests.', []) record = Record.new( desired, 'a', { 'ttl': 30, 'type': 'A', 'value': '1.2.3.4', 'geo': { 'NA': ['2.2.3.4'], } }) desired.add_record(record) extra = provider._extra_changes(desired, []) self.assertEquals(0, len(extra)) stubber.assert_no_pending_responses()
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()
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_unsupporting(self): class NoAaaaProvider(object): id = 'no-aaaa' SUPPORTS_GEO = False def supports(self, record): return record._type != 'AAAA' current = Zone('unit.tests.', []) desired = Zone('unit.tests.', []) a = ARecord(desired, 'a', {'ttl': 42, 'value': '1.1.1.1'}) desired.add_record(a) aaaa = AaaaRecord(desired, 'b', {'ttl': 42, 'value': '1:1:1::1'}) desired.add_record(aaaa) # Only create the supported A, not the AAAA changes = current.changes(desired, NoAaaaProvider()) self.assertEquals(1, len(changes)) self.assertIsInstance(changes[0], Create) # Only delete the supported A, not the AAAA changes = desired.changes(current, NoAaaaProvider()) self.assertEquals(1, len(changes)) self.assertIsInstance(changes[0], Delete)
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)
def test_not_included_records(self): zone_normal = Zone('unit.tests.', []) zone_included = Zone('unit.tests.', []) zone_missing = Zone('unit.tests.', []) normal = Record.new(zone_normal, 'www', { 'ttl': 60, 'type': 'A', 'value': '9.9.9.9', }) zone_normal.add_record(normal) included = Record.new(zone_included, 'www', { 'octodns': { 'included': ['not-here'] }, 'ttl': 60, 'type': 'A', 'value': '9.9.9.9', }) zone_included.add_record(included) provider = SimpleProvider() self.assertFalse(zone_normal.changes(zone_included, provider)) self.assertTrue(zone_normal.changes(zone_missing, provider)) self.assertFalse(zone_included.changes(zone_normal, provider)) self.assertFalse(zone_included.changes(zone_missing, provider)) self.assertTrue(zone_missing.changes(zone_normal, provider)) self.assertFalse(zone_missing.changes(zone_included, provider))
def test_apply_not_found(self): provider = EasyDNSProvider('test', 'token', 'apikey') wanted = Zone('unit.tests.', []) wanted.add_record(Record.new(wanted, 'test1', { "name": "test1", "ttl": 300, "type": "A", "value": "1.2.3.4", })) with requests_mock() as mock: base = 'https://rest.easydns.net/' mock.get('{}{}'.format(base, 'domain/unit.tests'), status_code=404, text='{"id":"not_found","message":"The resource you ' 'were accessing could not be found."}') mock.put('{}{}'.format(base, 'domains/add/unit.tests'), status_code=200, text='{"id":"OK","message":"Zone created."}') mock.get('{}{}'.format(base, 'zones/records/parsed/unit.tests'), status_code=404, text='{"id":"not_found","message":"The resource you ' 'were accessing could not be found."}') mock.get('{}{}'.format(base, 'zones/records/all/unit.tests'), status_code=404, text='{"id":"not_found","message":"The resource you ' 'were accessing could not be found."}') plan = provider.plan(wanted) self.assertFalse(plan.exists) self.assertEquals(1, len(plan.changes)) with self.assertRaises(Exception) as ctx: provider.apply(plan) self.assertEquals('Not Found', text_type(ctx.exception))
def test_base_provider(self): with self.assertRaises(NotImplementedError) as ctx: BaseProvider('base') self.assertEquals('Abstract base class, log property missing', ctx.exception.message) class HasLog(BaseProvider): log = getLogger('HasLog') with self.assertRaises(NotImplementedError) as ctx: HasLog('haslog') self.assertEquals('Abstract base class, SUPPORTS_GEO property missing', ctx.exception.message) class HasSupportsGeo(HasLog): SUPPORTS_GEO = False zone = Zone('unit.tests.', []) with self.assertRaises(NotImplementedError) as ctx: HasSupportsGeo('hassupportesgeo').populate(zone) self.assertEquals('Abstract base class, SUPPORTS property missing', ctx.exception.message) class HasSupports(HasSupportsGeo): SUPPORTS = set(('A',)) with self.assertRaises(NotImplementedError) as ctx: HasSupports('hassupportes').populate(zone) self.assertEquals('Abstract base class, populate method missing', ctx.exception.message) class HasPopulate(HasSupports): def populate(self, zone, target=False, lenient=False): zone.add_record(Record.new(zone, '', { 'ttl': 60, 'type': 'A', 'value': '2.3.4.5' })) zone.add_record(Record.new(zone, 'going', { 'ttl': 60, 'type': 'A', 'value': '3.4.5.6' })) zone.add_record(Record.new(zone, '', { 'ttl': 60, 'type': 'A', 'value': '1.2.3.4' })) self.assertTrue(HasSupports('hassupportesgeo') .supports(list(zone.records)[0])) plan = HasPopulate('haspopulate').plan(zone) self.assertEquals(2, len(plan.changes)) with self.assertRaises(NotImplementedError) as ctx: HasPopulate('haspopulate').apply(plan) self.assertEquals('Abstract base class, _apply method missing', ctx.exception.message)
def test_plan_disappearing_ns_records(self): expected = Zone('unit.tests.', []) expected.add_record( Record.new(expected, '', { 'type': 'NS', 'ttl': 600, 'values': ['8.8.8.8.', '9.9.9.9.'] })) expected.add_record( Record.new(expected, 'sub', { 'type': 'NS', 'ttl': 600, 'values': ['8.8.8.8.', '9.9.9.9.'] })) with requests_mock() as mock: mock.get(re.compile('domains$'), status_code=200, text=LIST_DOMAINS_RESPONSE) mock.get(re.compile('records'), status_code=200, text=EMPTY_TEXT) plan = self.provider.plan(expected) self.assertTrue(mock.called) self.assertTrue(plan.exists) # OctoDNS does not propagate top-level NS records. self.assertEquals(1, len(plan.changes))
def _test_apply_with_data(self, data): expected = Zone('unit.tests.', []) for record in data.OtherRecords: expected.add_record( Record.new(expected, record['subdomain'], record['data'])) with requests_mock() as list_mock: list_mock.get(re.compile('domains$'), status_code=200, text=LIST_DOMAINS_RESPONSE) list_mock.get(re.compile('records'), status_code=200, json=data.OwnRecords) plan = self.provider.plan(expected) self.assertTrue(list_mock.called) if not data.ExpectChanges: self.assertFalse(plan) return with requests_mock() as mock: called = set() def make_assert_sending_right_body(expected): def _assert_sending_right_body(request, _context): called.add(request.method) if request.method != 'DELETE': self.assertEqual(request.headers['content-type'], 'application/json') self.assertDictEqual(expected, json.loads(request.body)) else: parts = urlparse(request.url) self.assertEqual(expected, parts.query) return '' return _assert_sending_right_body mock.get(re.compile('domains$'), status_code=200, text=LIST_DOMAINS_RESPONSE) mock.post(re.compile('domains/.*/records$'), status_code=202, text=make_assert_sending_right_body( data.ExpectedAdditions)) mock.delete(re.compile('domains/.*/records?.*'), status_code=202, text=make_assert_sending_right_body( data.ExpectedDeletions)) mock.put(re.compile('domains/.*/records$'), status_code=202, text=make_assert_sending_right_body(data.ExpectedUpdates)) self.provider.apply(plan) self.assertTrue(data.ExpectedAdditions is None or "POST" in called) self.assertTrue(data.ExpectedDeletions is None or "DELETE" in called) self.assertTrue(data.ExpectedUpdates is None or "PUT" in called)
def test_process_desired_zone(self): provider = HelperProvider('test') # SUPPORTS_MULTIVALUE_PTR provider.SUPPORTS_MULTIVALUE_PTR = False zone1 = Zone('unit.tests.', []) record1 = Record.new(zone1, 'ptr', { 'type': 'PTR', 'ttl': 3600, 'values': ['foo.com.', 'bar.com.'], }) zone1.add_record(record1) zone2 = provider._process_desired_zone(zone1.copy()) record2 = list(zone2.records)[0] self.assertEqual(len(record2.values), 1) provider.SUPPORTS_MULTIVALUE_PTR = True zone2 = provider._process_desired_zone(zone1.copy()) record2 = list(zone2.records)[0] from pprint import pprint pprint([record1, record2]) self.assertEqual(len(record2.values), 2) # SUPPORTS_DYNAMIC provider.SUPPORTS_DYNAMIC = False zone1 = Zone('unit.tests.', []) record1 = Record.new( zone1, 'a', { 'dynamic': { 'pools': { 'one': { 'values': [{ 'value': '1.1.1.1', }], }, }, 'rules': [{ 'pool': 'one', }], }, 'type': 'A', 'ttl': 3600, 'values': ['2.2.2.2'], }) self.assertTrue(record1.dynamic) zone1.add_record(record1) zone2 = provider._process_desired_zone(zone1.copy()) record2 = list(zone2.records)[0] self.assertFalse(record2.dynamic) provider.SUPPORTS_DYNAMIC = True zone2 = provider._process_desired_zone(zone1.copy()) record2 = list(zone2.records)[0] self.assertTrue(record2.dynamic)
def test_extra_change_no_health_check(self): provider, stubber = self._get_stubbed_provider() list_hosted_zones_resp = { 'HostedZones': [{ 'Name': 'unit.tests.', 'Id': 'z42', 'CallerReference': 'abc', }], 'Marker': 'm', 'IsTruncated': False, 'MaxItems': '100', } stubber.add_response('list_hosted_zones', list_hosted_zones_resp, {}) # record with geo and no health check returns change existing = Zone('unit.tests.', []) record = Record.new( existing, 'a', { 'ttl': 30, 'type': 'A', 'value': '1.2.3.4', 'geo': { 'NA': ['2.2.3.4'], } }) existing.add_record(record) list_resource_record_sets_resp = { 'ResourceRecordSets': [{ 'Name': 'a.unit.tests.', 'Type': 'A', 'GeoLocation': { 'ContinentCode': 'NA', }, 'ResourceRecords': [{ 'Value': '2.2.3.4', }], 'TTL': 61, }], 'IsTruncated': False, 'MaxItems': '100', } stubber.add_response('list_resource_record_sets', list_resource_record_sets_resp, {'HostedZoneId': 'z42'}) extra = provider._extra_changes(existing, []) self.assertEquals(1, len(extra)) stubber.assert_no_pending_responses()
def test_include_change(self): zone = Zone('unit.tests.', []) record = Record.new(zone, 'a', { 'ttl': 30, 'type': 'A', 'value': '1.2.3.4', }) zone.add_record(record) provider = HelperProvider([], include_change_callback=lambda c: False) plan = provider.plan(zone) # We filtered out the only change self.assertFalse(plan)
def test_process_desired_zone(self): zone1 = Zone('unit.tests.', []) record1 = Record.new(zone1, 'ptr', { 'type': 'PTR', 'ttl': 3600, 'values': ['foo.com.', 'bar.com.'], }) zone1.add_record(record1) zone2 = HelperProvider('hasptr')._process_desired_zone(zone1) record2 = list(zone2.records)[0] self.assertEqual(len(record2.values), 1)
def test_copy(self): zone = Zone('unit.tests.', []) a = ARecord(zone, 'a', {'ttl': 42, 'value': '1.1.1.1'}) zone.add_record(a) b = ARecord(zone, 'b', {'ttl': 42, 'value': '1.1.1.2'}) zone.add_record(b) # Sanity check self.assertEqualsNameAndValues(set((a, b)), zone.records) copy = zone.copy() # We have an origin set and it is the source/original zone self.assertEquals(zone, copy._origin) # Our records are zone's records to start (references) self.assertEqualsNameAndValues(zone.records, copy.records) # If we try and change something that's already there we realize and # then get an error about a duplicate b_prime = ARecord(zone, 'b', {'ttl': 42, 'value': '1.1.1.3'}) with self.assertRaises(DuplicateRecordException): copy.add_record(b_prime) self.assertIsNone(copy._origin) # Unchanged, straight copies self.assertEqualsNameAndValues(zone.records, copy.records) # If we add with replace things will be realized and the record will # have changed copy = zone.copy() copy.add_record(b_prime, replace=True) self.assertIsNone(copy._origin) self.assertEqualsNameAndValues(set((a, b_prime)), copy.records) # If we add another record, things are reliazed and it has been added copy = zone.copy() c = ARecord(zone, 'c', {'ttl': 42, 'value': '1.1.1.3'}) copy.add_record(c) self.assertEqualsNameAndValues(set((a, b, c)), copy.records) # If we remove a record, things are reliazed and it has been removed copy = zone.copy() copy.remove_record(a) self.assertEqualsNameAndValues(set((b, )), copy.records) # Re-realizing is a noop copy = zone.copy() # Happens the first time self.assertTrue(copy.hydrate()) # Doesn't the second self.assertFalse(copy.hydrate())
def test_populate_normal_sub1(self): got = Zone('asdf.subtest.com.', []) self.source.populate(got) self.assertEquals(1, len(got.records)) expected = Zone('asdf.subtest.com.', []) for name, data in (('a3', { 'type': 'A', 'ttl': 3600, 'values': ['10.2.3.7'], }), ): record = Record.new(expected, name, data) expected.add_record(record) changes = expected.changes(got, SimpleProvider()) self.assertEquals([], changes)
def test_cdn_alias(self): provider = CloudflareProvider('test', 'email', 'token', True) # A CNAME for us to transform to ALIAS provider.zone_records = Mock(return_value=[ { "id": "fc12ab34cd5611334422ab3322997642", "type": "CNAME", "name": "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 } }, ]) zone = Zone('unit.tests.', []) provider.populate(zone) self.assertEquals(1, len(zone.records)) record = list(zone.records)[0] self.assertEquals('', record.name) self.assertEquals('unit.tests.', record.fqdn) self.assertEquals('ALIAS', record._type) self.assertEquals('unit.tests.cdn.cloudflare.net.', record.value) # CDN enabled records can't be updated, we don't know the real values # never point a Cloudflare record to itsself. wanted = Zone('unit.tests.', []) wanted.add_record( Record.new( wanted, '', { 'ttl': 300, 'type': 'ALIAS', 'value': 'change.unit.tests.cdn.cloudflare.net.' })) plan = provider.plan(wanted) self.assertEquals(False, hasattr(plan, 'changes'))
def test_small_change(self): provider = PowerDnsProvider('test', 'non.existent', 'api-key') expected = Zone('unit.tests.', []) source = YamlProvider('test', join(dirname(__file__), 'config')) source.populate(expected) self.assertEquals(23, len(expected.records)) # A small change to a single record with requests_mock() as mock: mock.get(ANY, status_code=200, text=FULL_TEXT) mock.get('http://non.existent:8081/api/v1/servers/localhost', status_code=200, json={'version': '4.1.0'}) 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_plan_with_root_ns(self): zone = Zone('unit.tests.', []) record = Record.new(zone, '', { 'ttl': 30, 'type': 'NS', 'value': '1.2.3.4.' }) zone.add_record(record) # No root NS support, no change, thus no plan provider = HelperProvider() self.assertEqual(None, provider.plan(zone)) # set Support root NS records, see the record provider.SUPPORTS_ROOT_NS = True plan = provider.plan(zone) self.assertTrue(plan) self.assertEqual(1, len(plan.changes))
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()
def test_change_record(self, fake_http): exist_record = [ self.aaaa_record, { "content": "6.6.5.7", "ttl": 100, "type": "A", "id": 100001, "name": "delete.unit.tests" }, { "content": "9.8.2.1", "ttl": 100, "type": "A", "id": 100002, "name": "unit.tests" } ] # exist fake_http.get('{}/unit.tests/records/'.format(self.API_URL), json=exist_record) fake_http.get('{}/'.format(self.API_URL), json=self.domain) fake_http.get('{}/100000/records/'.format(self.API_URL), json=exist_record) fake_http.head('{}/unit.tests/records/'.format(self.API_URL), headers={'X-Total-Count': str(len(exist_record))}) fake_http.head('{}/'.format(self.API_URL), headers={'X-Total-Count': str(len(self.domain))}) fake_http.head('{}/100000/records/'.format(self.API_URL), headers={'X-Total-Count': str(len(exist_record))}) fake_http.post('{}/100000/records/'.format(self.API_URL), json=list()) fake_http.delete('{}/100000/records/100001'.format(self.API_URL), text="") fake_http.delete('{}/100000/records/100002'.format(self.API_URL), text="") provider = SelectelProvider(123, 'test_token') zone = Zone('unit.tests.', []) for record in self.expected: zone.add_record(record) plan = provider.plan(zone) self.assertEquals(8, len(plan.changes)) self.assertEquals(8, provider.apply(plan))
def test_apply(self, fake_http): fake_http.get(f'{self.API_URL}/unit.tests/records/', json=list()) fake_http.get(f'{self.API_URL}/', json=self.domain) fake_http.head(f'{self.API_URL}/unit.tests/records/', headers={'X-Total-Count': '0'}) fake_http.head(f'{self.API_URL}/', headers={'X-Total-Count': str(len(self.domain))}) fake_http.post(f'{self.API_URL}/100000/records/', json=list()) provider = SelectelProvider(123, 'test_token') zone = Zone('unit.tests.', []) for record in self.expected: zone.add_record(record) plan = provider.plan(zone) self.assertEquals(8, len(plan.changes)) self.assertEquals(8, provider.apply(plan))
def test_no_extra_changes(self): provider, stubber = self._get_stubbed_provider() list_hosted_zones_resp = { 'HostedZones': [{ 'Name': 'unit.tests.', 'Id': 'z42', 'CallerReference': 'abc', }], 'Marker': 'm', 'IsTruncated': False, 'MaxItems': '100', } stubber.add_response('list_hosted_zones', list_hosted_zones_resp, {}) # empty is empty existing = Zone('unit.tests.', []) extra = provider._extra_changes(existing, []) self.assertEquals([], extra) stubber.assert_no_pending_responses() # single record w/o geo is empty existing = Zone('unit.tests.', []) record = Record.new(existing, 'a', { 'ttl': 30, 'type': 'A', 'value': '1.2.3.4', }) existing.add_record(record) extra = provider._extra_changes(existing, []) self.assertEquals([], extra) stubber.assert_no_pending_responses() # short-circuit for unknown zone other = Zone('other.tests.', []) extra = provider._extra_changes(other, []) self.assertEquals([], extra) stubber.assert_no_pending_responses()
def octodns_test_zone(): '''Load the unit.tests zone config into an octodns.zone.Zone object.''' zone = Zone('unit.tests.', []) source = YamlProvider('test', join(dirname(__file__), 'config')) source.populate(zone) # Replace the unit test fixture's NS record with one of ours. remove_octodns_record(zone, '', 'NS') zone.add_record( Record.new( zone, '', { 'ttl': 3600, 'type': 'NS', 'values': [ 'dns1.p01.nsone.net.', 'dns2.p01.nsone.net.', 'dns3.p01.nsone.net.', 'dns4.p01.nsone.net.' ] })) return zone
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_populate_invalid_record(self, fake_http): more_record = self.api_record more_record.append({ "name": "unit.tests", "id": 100001, "content": "support.unit.tests.", "ttl": 300, "ns": "ns1.unit.tests", "type": "SOA", "email": "*****@*****.**" }) zone = Zone('unit.tests.', []) fake_http.get('{}/unit.tests/records/'.format(self.API_URL), json=more_record) fake_http.get('{}/'.format(self.API_URL), json=self.domain) fake_http.head('{}/unit.tests/records/'.format(self.API_URL), headers={'X-Total-Count': str(len(self.api_record))}) fake_http.head('{}/'.format(self.API_URL), headers={'X-Total-Count': str(len(self.domain))}) zone.add_record( Record.new( self.zone, 'unsup', { 'ttl': 200, 'type': 'NAPTR', 'value': { 'order': 40, 'preference': 70, 'flags': 'U', 'service': 'SIP+D2U', 'regexp': '!^.*$!sip:[email protected]!', 'replacement': '.', } })) provider = SelectelProvider(123, 'secret_token') provider.populate(zone) self.assertNotEqual(self.expected, zone.records)
def test_cname_loop(self): source = EtcHostsProvider('test', path.join(dirname(__file__), 'config')) zone = Zone('unit.tests.', []) # We never populate anything, when acting as a source source.populate(zone, target=source) self.assertEquals(0, len(zone.records)) # Same if we're acting as a target source.populate(zone) self.assertEquals(0, len(zone.records)) record = Record.new(zone, 'start', { 'ttl': 60, 'type': 'CNAME', 'value': 'middle.unit.tests.', }) zone.add_record(record) record = Record.new(zone, 'middle', { 'ttl': 60, 'type': 'CNAME', 'value': 'loop.unit.tests.', }) zone.add_record(record) record = Record.new(zone, 'loop', { 'ttl': 60, 'type': 'CNAME', 'value': 'start.unit.tests.', }) zone.add_record(record) with TemporaryDirectory() as td: # Add some subdirs to make sure that it can create them directory = path.join(td.dirname, 'sub', 'dir') hosts_file = path.join(directory, 'unit.tests.hosts') target = EtcHostsProvider('test', directory) # We add everything plan = target.plan(zone) self.assertEquals(len(zone.records), len(plan.changes)) self.assertFalse(isfile(hosts_file)) # Now actually do it self.assertEquals(len(zone.records), target.apply(plan)) self.assertTrue(isfile(hosts_file)) with open(hosts_file) as fh: data = fh.read() print(data) self.assertTrue('# loop.unit.tests -> start.unit.tests ' '**loop**' in data) self.assertTrue('# middle.unit.tests -> loop.unit.tests ' '**loop**' in data) self.assertTrue('# start.unit.tests -> middle.unit.tests ' '**loop**' in data)