|
|
@ -316,6 +316,7 @@ class TestNs1Provider(TestCase): |
|
|
|
|
|
|
|
|
# Fails, general error |
|
|
# Fails, general error |
|
|
zone_retrieve_mock.reset_mock() |
|
|
zone_retrieve_mock.reset_mock() |
|
|
|
|
|
record_retrieve_mock.reset_mock() |
|
|
zone_create_mock.reset_mock() |
|
|
zone_create_mock.reset_mock() |
|
|
zone_retrieve_mock.side_effect = ResourceException('boom') |
|
|
zone_retrieve_mock.side_effect = ResourceException('boom') |
|
|
with self.assertRaises(ResourceException) as ctx: |
|
|
with self.assertRaises(ResourceException) as ctx: |
|
|
@ -324,6 +325,7 @@ class TestNs1Provider(TestCase): |
|
|
|
|
|
|
|
|
# Fails, bad auth |
|
|
# Fails, bad auth |
|
|
zone_retrieve_mock.reset_mock() |
|
|
zone_retrieve_mock.reset_mock() |
|
|
|
|
|
record_retrieve_mock.reset_mock() |
|
|
zone_create_mock.reset_mock() |
|
|
zone_create_mock.reset_mock() |
|
|
zone_retrieve_mock.side_effect = \ |
|
|
zone_retrieve_mock.side_effect = \ |
|
|
ResourceException('server error: zone not found') |
|
|
ResourceException('server error: zone not found') |
|
|
@ -334,6 +336,7 @@ class TestNs1Provider(TestCase): |
|
|
|
|
|
|
|
|
# non-existent zone, create |
|
|
# non-existent zone, create |
|
|
zone_retrieve_mock.reset_mock() |
|
|
zone_retrieve_mock.reset_mock() |
|
|
|
|
|
record_retrieve_mock.reset_mock() |
|
|
zone_create_mock.reset_mock() |
|
|
zone_create_mock.reset_mock() |
|
|
zone_retrieve_mock.side_effect = \ |
|
|
zone_retrieve_mock.side_effect = \ |
|
|
ResourceException('server error: zone not found') |
|
|
ResourceException('server error: zone not found') |
|
|
@ -364,6 +367,7 @@ class TestNs1Provider(TestCase): |
|
|
|
|
|
|
|
|
# Update & delete |
|
|
# Update & delete |
|
|
zone_retrieve_mock.reset_mock() |
|
|
zone_retrieve_mock.reset_mock() |
|
|
|
|
|
record_retrieve_mock.reset_mock() |
|
|
zone_create_mock.reset_mock() |
|
|
zone_create_mock.reset_mock() |
|
|
|
|
|
|
|
|
ns1_zone = { |
|
|
ns1_zone = { |
|
|
@ -389,7 +393,7 @@ class TestNs1Provider(TestCase): |
|
|
} |
|
|
} |
|
|
ns1_zone['records'][0]['short_answers'][0] = '2.2.2.2' |
|
|
ns1_zone['records'][0]['short_answers'][0] = '2.2.2.2' |
|
|
|
|
|
|
|
|
record_retrieve_mock.side_effect = [{ |
|
|
|
|
|
|
|
|
ns1_record = { |
|
|
"domain": "geo.unit.tests", |
|
|
"domain": "geo.unit.tests", |
|
|
"zone": "unit.tests", |
|
|
"zone": "unit.tests", |
|
|
"type": "A", |
|
|
"type": "A", |
|
|
@ -404,8 +408,9 @@ class TestNs1Provider(TestCase): |
|
|
], |
|
|
], |
|
|
'tier': 3, |
|
|
'tier': 3, |
|
|
'ttl': 34, |
|
|
'ttl': 34, |
|
|
}] |
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
record_retrieve_mock.side_effect = [ns1_record, ns1_record] |
|
|
zone_retrieve_mock.side_effect = [ns1_zone, ns1_zone] |
|
|
zone_retrieve_mock.side_effect = [ns1_zone, ns1_zone] |
|
|
plan = provider.plan(desired) |
|
|
plan = provider.plan(desired) |
|
|
self.assertEquals(3, len(plan.changes)) |
|
|
self.assertEquals(3, len(plan.changes)) |
|
|
@ -427,6 +432,8 @@ class TestNs1Provider(TestCase): |
|
|
None, |
|
|
None, |
|
|
] |
|
|
] |
|
|
|
|
|
|
|
|
|
|
|
record_retrieve_mock.side_effect = [ns1_record, ns1_record] |
|
|
|
|
|
zone_retrieve_mock.side_effect = [ns1_zone, ns1_zone] |
|
|
got_n = provider.apply(plan) |
|
|
got_n = provider.apply(plan) |
|
|
self.assertEquals(3, got_n) |
|
|
self.assertEquals(3, got_n) |
|
|
|
|
|
|
|
|
@ -926,6 +933,42 @@ class TestNs1ProviderDynamic(TestCase): |
|
|
monitors_delete_mock.assert_has_calls([call('mon-id2')]) |
|
|
monitors_delete_mock.assert_has_calls([call('mon-id2')]) |
|
|
notifylists_delete_mock.assert_has_calls([call('nl-id2')]) |
|
|
notifylists_delete_mock.assert_has_calls([call('nl-id2')]) |
|
|
|
|
|
|
|
|
|
|
|
@patch('octodns.provider.ns1.Ns1Provider._monitor_sync') |
|
|
|
|
|
@patch('octodns.provider.ns1.Ns1Provider._monitors_for') |
|
|
|
|
|
def test_params_for_dynamic_region_only(self, monitors_for_mock, |
|
|
|
|
|
monitor_sync_mock): |
|
|
|
|
|
provider = Ns1Provider('test', 'api-key') |
|
|
|
|
|
|
|
|
|
|
|
# pre-fill caches to avoid extranious calls (things we're testing |
|
|
|
|
|
# elsewhere) |
|
|
|
|
|
provider._client._datasource_id = 'foo' |
|
|
|
|
|
provider._client._feeds_for_monitors = { |
|
|
|
|
|
'mon-id': 'feed-id', |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
# provider._params_for_A() calls provider._monitors_for() and |
|
|
|
|
|
# provider._monitor_sync(). Mock their return values so that we don't |
|
|
|
|
|
# make NS1 API calls during tests |
|
|
|
|
|
monitors_for_mock.reset_mock() |
|
|
|
|
|
monitor_sync_mock.reset_mock() |
|
|
|
|
|
monitors_for_mock.side_effect = [{ |
|
|
|
|
|
'3.4.5.6': 'mid-3', |
|
|
|
|
|
}] |
|
|
|
|
|
monitor_sync_mock.side_effect = [ |
|
|
|
|
|
('mid-1', 'fid-1'), |
|
|
|
|
|
('mid-2', 'fid-2'), |
|
|
|
|
|
('mid-3', 'fid-3'), |
|
|
|
|
|
] |
|
|
|
|
|
|
|
|
|
|
|
rule0 = self.record.data['dynamic']['rules'][0] |
|
|
|
|
|
saved_geos = rule0['geos'] |
|
|
|
|
|
rule0['geos'] = ['AF', 'EU'] |
|
|
|
|
|
ret, _ = provider._params_for_A(self.record) |
|
|
|
|
|
self.assertEquals(ret['filters'], |
|
|
|
|
|
Ns1Provider._FILTER_CHAIN_WITH_REGION(provider, |
|
|
|
|
|
True)) |
|
|
|
|
|
rule0['geos'] = saved_geos |
|
|
|
|
|
|
|
|
@patch('octodns.provider.ns1.Ns1Provider._monitor_sync') |
|
|
@patch('octodns.provider.ns1.Ns1Provider._monitor_sync') |
|
|
@patch('octodns.provider.ns1.Ns1Provider._monitors_for') |
|
|
@patch('octodns.provider.ns1.Ns1Provider._monitors_for') |
|
|
def test_params_for_dynamic_oceania(self, monitors_for_mock, |
|
|
def test_params_for_dynamic_oceania(self, monitors_for_mock, |
|
|
@ -961,6 +1004,11 @@ class TestNs1ProviderDynamic(TestCase): |
|
|
ret, _ = provider._params_for_A(self.record) |
|
|
ret, _ = provider._params_for_A(self.record) |
|
|
self.assertEquals(set(ret['regions']['lhr']['meta']['country']), |
|
|
self.assertEquals(set(ret['regions']['lhr']['meta']['country']), |
|
|
Ns1Provider._CONTINENT_TO_LIST_OF_COUNTRIES['OC']) |
|
|
Ns1Provider._CONTINENT_TO_LIST_OF_COUNTRIES['OC']) |
|
|
|
|
|
# When rules has 'OC', it is converted to list of countries in the |
|
|
|
|
|
# params. Look if the returned filters is the filter chain with country |
|
|
|
|
|
self.assertEquals(ret['filters'], |
|
|
|
|
|
Ns1Provider._FILTER_CHAIN_WITH_COUNTRY(provider, |
|
|
|
|
|
True)) |
|
|
rule0['geos'] = saved_geos |
|
|
rule0['geos'] = saved_geos |
|
|
|
|
|
|
|
|
@patch('octodns.provider.ns1.Ns1Provider._monitor_sync') |
|
|
@patch('octodns.provider.ns1.Ns1Provider._monitor_sync') |
|
|
@ -987,7 +1035,14 @@ class TestNs1ProviderDynamic(TestCase): |
|
|
] |
|
|
] |
|
|
# This indirectly calls into _params_for_dynamic_A and tests the |
|
|
# This indirectly calls into _params_for_dynamic_A and tests the |
|
|
# handling to get there |
|
|
# handling to get there |
|
|
provider._params_for_A(self.record) |
|
|
|
|
|
|
|
|
ret, _ = provider._params_for_A(self.record) |
|
|
|
|
|
|
|
|
|
|
|
# Given that self.record has both country and region in the rules, |
|
|
|
|
|
# the returned filter chain should be one with region and country |
|
|
|
|
|
self.assertEquals(ret['filters'], |
|
|
|
|
|
Ns1Provider._FILTER_CHAIN_WITH_REGION_AND_COUNTRY( |
|
|
|
|
|
provider, True)) |
|
|
|
|
|
|
|
|
monitors_for_mock.assert_has_calls([call(self.record)]) |
|
|
monitors_for_mock.assert_has_calls([call(self.record)]) |
|
|
monitors_sync_mock.assert_has_calls([ |
|
|
monitors_sync_mock.assert_has_calls([ |
|
|
call(self.record, '1.2.3.4', None), |
|
|
call(self.record, '1.2.3.4', None), |
|
|
@ -1012,7 +1067,7 @@ class TestNs1ProviderDynamic(TestCase): |
|
|
ns1_record = { |
|
|
ns1_record = { |
|
|
'answers': [], |
|
|
'answers': [], |
|
|
'domain': 'unit.tests', |
|
|
'domain': 'unit.tests', |
|
|
'filters': Ns1Provider._DYNAMIC_FILTERS, |
|
|
|
|
|
|
|
|
'filters': Ns1Provider._BASIC_FILTER_CHAIN(provider, True), |
|
|
'regions': {}, |
|
|
'regions': {}, |
|
|
'ttl': 42, |
|
|
'ttl': 42, |
|
|
} |
|
|
} |
|
|
@ -1028,6 +1083,8 @@ class TestNs1ProviderDynamic(TestCase): |
|
|
}, data) |
|
|
}, data) |
|
|
|
|
|
|
|
|
# Test out a small, but realistic setup that covers all the options |
|
|
# Test out a small, but realistic setup that covers all the options |
|
|
|
|
|
# We have country and region in the test config |
|
|
|
|
|
filters = provider._get_updated_filter_chain(True, True) |
|
|
ns1_record = { |
|
|
ns1_record = { |
|
|
'answers': [{ |
|
|
'answers': [{ |
|
|
'answer': ['3.4.5.6'], |
|
|
'answer': ['3.4.5.6'], |
|
|
@ -1068,7 +1125,7 @@ class TestNs1ProviderDynamic(TestCase): |
|
|
'region': 'iad', |
|
|
'region': 'iad', |
|
|
}], |
|
|
}], |
|
|
'domain': 'unit.tests', |
|
|
'domain': 'unit.tests', |
|
|
'filters': Ns1Provider._DYNAMIC_FILTERS, |
|
|
|
|
|
|
|
|
'filters': filters, |
|
|
'regions': { |
|
|
'regions': { |
|
|
'lhr': { |
|
|
'lhr': { |
|
|
'meta': { |
|
|
'meta': { |
|
|
@ -1144,20 +1201,46 @@ class TestNs1ProviderDynamic(TestCase): |
|
|
self.assertTrue( |
|
|
self.assertTrue( |
|
|
'OC-{}'.format(c) in data4['dynamic']['rules'][0]['geos']) |
|
|
'OC-{}'.format(c) in data4['dynamic']['rules'][0]['geos']) |
|
|
|
|
|
|
|
|
|
|
|
@patch('ns1.rest.records.Records.retrieve') |
|
|
|
|
|
@patch('ns1.rest.zones.Zones.retrieve') |
|
|
@patch('octodns.provider.ns1.Ns1Provider._monitors_for') |
|
|
@patch('octodns.provider.ns1.Ns1Provider._monitors_for') |
|
|
def test_extra_changes(self, monitors_for_mock): |
|
|
|
|
|
|
|
|
def test_extra_changes(self, monitors_for_mock, zones_retrieve_mock, |
|
|
|
|
|
records_retrieve_mock): |
|
|
provider = Ns1Provider('test', 'api-key') |
|
|
provider = Ns1Provider('test', 'api-key') |
|
|
|
|
|
|
|
|
desired = Zone('unit.tests.', []) |
|
|
desired = Zone('unit.tests.', []) |
|
|
|
|
|
|
|
|
# Empty zone and no changes |
|
|
# Empty zone and no changes |
|
|
monitors_for_mock.reset_mock() |
|
|
monitors_for_mock.reset_mock() |
|
|
|
|
|
zones_retrieve_mock.reset_mock() |
|
|
|
|
|
records_retrieve_mock.reset_mock() |
|
|
|
|
|
|
|
|
extra = provider._extra_changes(desired, []) |
|
|
extra = provider._extra_changes(desired, []) |
|
|
self.assertFalse(extra) |
|
|
self.assertFalse(extra) |
|
|
monitors_for_mock.assert_not_called() |
|
|
monitors_for_mock.assert_not_called() |
|
|
|
|
|
|
|
|
# Simple record, ignored |
|
|
|
|
|
|
|
|
# Non-existent zone. No changes |
|
|
monitors_for_mock.reset_mock() |
|
|
monitors_for_mock.reset_mock() |
|
|
|
|
|
zones_retrieve_mock.side_effect = \ |
|
|
|
|
|
ResourceException('server error: zone not found') |
|
|
|
|
|
records_retrieve_mock.reset_mock() |
|
|
|
|
|
extra = provider._extra_changes(desired, []) |
|
|
|
|
|
self.assertFalse(extra) |
|
|
|
|
|
|
|
|
|
|
|
# Unexpected exception message |
|
|
|
|
|
zones_retrieve_mock.reset_mock() |
|
|
|
|
|
zones_retrieve_mock.side_effect = ResourceException('boom') |
|
|
|
|
|
with self.assertRaises(ResourceException) as ctx: |
|
|
|
|
|
extra = provider._extra_changes(desired, []) |
|
|
|
|
|
self.assertEquals(zones_retrieve_mock.side_effect, ctx.exception) |
|
|
|
|
|
|
|
|
|
|
|
# Simple record, ignored, filter update lookups ignored |
|
|
|
|
|
monitors_for_mock.reset_mock() |
|
|
|
|
|
zones_retrieve_mock.reset_mock() |
|
|
|
|
|
records_retrieve_mock.reset_mock() |
|
|
|
|
|
zones_retrieve_mock.side_effect = \ |
|
|
|
|
|
ResourceException('server error: zone not found') |
|
|
|
|
|
|
|
|
simple = Record.new(desired, '', { |
|
|
simple = Record.new(desired, '', { |
|
|
'ttl': 32, |
|
|
'ttl': 32, |
|
|
'type': 'A', |
|
|
'type': 'A', |
|
|
@ -1200,6 +1283,8 @@ class TestNs1ProviderDynamic(TestCase): |
|
|
|
|
|
|
|
|
# untouched, but everything in sync so no change needed |
|
|
# untouched, but everything in sync so no change needed |
|
|
monitors_for_mock.reset_mock() |
|
|
monitors_for_mock.reset_mock() |
|
|
|
|
|
zones_retrieve_mock.reset_mock() |
|
|
|
|
|
records_retrieve_mock.reset_mock() |
|
|
# Generate what we expect to have |
|
|
# Generate what we expect to have |
|
|
gend = provider._monitor_gen(dynamic, '1.2.3.4') |
|
|
gend = provider._monitor_gen(dynamic, '1.2.3.4') |
|
|
gend.update({ |
|
|
gend.update({ |
|
|
@ -1218,6 +1303,8 @@ class TestNs1ProviderDynamic(TestCase): |
|
|
# If we don't have a notify list we're broken and we'll expect to see |
|
|
# If we don't have a notify list we're broken and we'll expect to see |
|
|
# an Update |
|
|
# an Update |
|
|
monitors_for_mock.reset_mock() |
|
|
monitors_for_mock.reset_mock() |
|
|
|
|
|
zones_retrieve_mock.reset_mock() |
|
|
|
|
|
records_retrieve_mock.reset_mock() |
|
|
del gend['notify_list'] |
|
|
del gend['notify_list'] |
|
|
monitors_for_mock.side_effect = [{ |
|
|
monitors_for_mock.side_effect = [{ |
|
|
'1.2.3.4': gend, |
|
|
'1.2.3.4': gend, |
|
|
@ -1232,6 +1319,8 @@ class TestNs1ProviderDynamic(TestCase): |
|
|
# Add notify_list back and change the healthcheck protocol, we'll still |
|
|
# Add notify_list back and change the healthcheck protocol, we'll still |
|
|
# expect to see an update |
|
|
# expect to see an update |
|
|
monitors_for_mock.reset_mock() |
|
|
monitors_for_mock.reset_mock() |
|
|
|
|
|
zones_retrieve_mock.reset_mock() |
|
|
|
|
|
records_retrieve_mock.reset_mock() |
|
|
gend['notify_list'] = 'xyz' |
|
|
gend['notify_list'] = 'xyz' |
|
|
dynamic._octodns['healthcheck']['protocol'] = 'HTTPS' |
|
|
dynamic._octodns['healthcheck']['protocol'] = 'HTTPS' |
|
|
del gend['notify_list'] |
|
|
del gend['notify_list'] |
|
|
@ -1247,10 +1336,74 @@ class TestNs1ProviderDynamic(TestCase): |
|
|
|
|
|
|
|
|
# If it's in the changed list, it'll be ignored |
|
|
# If it's in the changed list, it'll be ignored |
|
|
monitors_for_mock.reset_mock() |
|
|
monitors_for_mock.reset_mock() |
|
|
|
|
|
zones_retrieve_mock.reset_mock() |
|
|
|
|
|
records_retrieve_mock.reset_mock() |
|
|
extra = provider._extra_changes(desired, [update]) |
|
|
extra = provider._extra_changes(desired, [update]) |
|
|
self.assertFalse(extra) |
|
|
self.assertFalse(extra) |
|
|
monitors_for_mock.assert_not_called() |
|
|
monitors_for_mock.assert_not_called() |
|
|
|
|
|
|
|
|
|
|
|
# Test changes in filters |
|
|
|
|
|
|
|
|
|
|
|
# No change in filters |
|
|
|
|
|
monitors_for_mock.reset_mock() |
|
|
|
|
|
zones_retrieve_mock.reset_mock() |
|
|
|
|
|
records_retrieve_mock.reset_mock() |
|
|
|
|
|
ns1_zone = { |
|
|
|
|
|
'records': [{ |
|
|
|
|
|
"domain": "dyn.unit.tests", |
|
|
|
|
|
"zone": "unit.tests", |
|
|
|
|
|
"type": "A", |
|
|
|
|
|
"tier": 3, |
|
|
|
|
|
"filters": Ns1Provider._BASIC_FILTER_CHAIN(provider, True) |
|
|
|
|
|
}], |
|
|
|
|
|
} |
|
|
|
|
|
monitors_for_mock.side_effect = [{}] |
|
|
|
|
|
zones_retrieve_mock.side_effect = [ns1_zone] |
|
|
|
|
|
records_retrieve_mock.side_effect = ns1_zone['records'] |
|
|
|
|
|
extra = provider._extra_changes(desired, []) |
|
|
|
|
|
self.assertFalse(extra) |
|
|
|
|
|
|
|
|
|
|
|
# filters need an update |
|
|
|
|
|
monitors_for_mock.reset_mock() |
|
|
|
|
|
zones_retrieve_mock.reset_mock() |
|
|
|
|
|
records_retrieve_mock.reset_mock() |
|
|
|
|
|
ns1_zone = { |
|
|
|
|
|
'records': [{ |
|
|
|
|
|
"domain": "dyn.unit.tests", |
|
|
|
|
|
"zone": "unit.tests", |
|
|
|
|
|
"type": "A", |
|
|
|
|
|
"tier": 3, |
|
|
|
|
|
"filters": Ns1Provider._BASIC_FILTER_CHAIN(provider, False) |
|
|
|
|
|
}], |
|
|
|
|
|
} |
|
|
|
|
|
monitors_for_mock.side_effect = [{}] |
|
|
|
|
|
zones_retrieve_mock.side_effect = [ns1_zone] |
|
|
|
|
|
records_retrieve_mock.side_effect = ns1_zone['records'] |
|
|
|
|
|
extra = provider._extra_changes(desired, []) |
|
|
|
|
|
self.assertTrue(extra) |
|
|
|
|
|
|
|
|
|
|
|
# Mixed disabled in filters. Raise Ns1Exception |
|
|
|
|
|
monitors_for_mock.reset_mock() |
|
|
|
|
|
zones_retrieve_mock.reset_mock() |
|
|
|
|
|
records_retrieve_mock.reset_mock() |
|
|
|
|
|
ns1_zone = { |
|
|
|
|
|
'records': [{ |
|
|
|
|
|
"domain": "dyn.unit.tests", |
|
|
|
|
|
"zone": "unit.tests", |
|
|
|
|
|
"type": "A", |
|
|
|
|
|
"tier": 3, |
|
|
|
|
|
"filters": Ns1Provider._BASIC_FILTER_CHAIN(provider, True) |
|
|
|
|
|
}], |
|
|
|
|
|
} |
|
|
|
|
|
del ns1_zone['records'][0]['filters'][0]['disabled'] |
|
|
|
|
|
monitors_for_mock.side_effect = [{}] |
|
|
|
|
|
zones_retrieve_mock.side_effect = [ns1_zone] |
|
|
|
|
|
records_retrieve_mock.side_effect = ns1_zone['records'] |
|
|
|
|
|
with self.assertRaises(Ns1Exception) as ctx: |
|
|
|
|
|
extra = provider._extra_changes(desired, []) |
|
|
|
|
|
self.assertTrue('Mixed disabled flag in filters' in |
|
|
|
|
|
text_type(ctx.exception)) |
|
|
|
|
|
|
|
|
DESIRED = Zone('unit.tests.', []) |
|
|
DESIRED = Zone('unit.tests.', []) |
|
|
|
|
|
|
|
|
SIMPLE = Record.new(DESIRED, 'sim', { |
|
|
SIMPLE = Record.new(DESIRED, 'sim', { |
|
|
|