1
0
mirror of https://github.com/github/octodns.git synced 2024-05-11 05:55:00 +00:00

Support reuse of default pool in rules in Ns1Provider

This commit is contained in:
Pavan Chandrashekar
2020-04-04 01:28:39 -07:00
parent 921ca5376c
commit a64ec99de8
2 changed files with 122 additions and 45 deletions

View File

@@ -468,8 +468,12 @@ class Ns1Provider(BaseProvider):
pools = defaultdict(lambda: {'fallback': None, 'values': []})
for answer in record['answers']:
# region (group name in the UI) is the pool name
# Shadow pools are a mechanism internal to this provider
# Skip them when constructing octoDNS records from ns1 records
pool_name = answer['region']
pool = pools[answer['region']]
if pool_name.endswith('_shadow'):
continue
pool = pools[pool_name]
meta = answer['meta']
value = text_type(answer['answer'][0])
@@ -492,6 +496,11 @@ class Ns1Provider(BaseProvider):
# examples currently where it would
rules = []
for pool_name, region in sorted(record['regions'].items()):
# Rules that refer to the default dynamic pool would have
# used the shadow pool name. Convert it back to the dynamic
# default pool name by stripping the '_shadow' in the pool name
if pool_name.endswith('_shadow'):
pool_name = pool_name[:-7]
meta = region['meta']
notes = self._parse_notes(meta.get('note', ''))
@@ -935,6 +944,49 @@ class Ns1Provider(BaseProvider):
notify_list_id = monitor['notify_list']
self._client.notifylists_delete(notify_list_id)
def _add_answers_for_pool(self, answers, default_answers, pool_name,
pool_label, pool_answers, pools, priority):
current_pool_name = pool_name
seen = set()
while current_pool_name and current_pool_name not in seen:
seen.add(current_pool_name)
pool = pools[current_pool_name]
for answer in pool_answers[current_pool_name]:
answer = {
'answer': answer['answer'],
'meta': {
'priority': priority,
'note': self._encode_notes({
'from': pool_label,
}),
'up': {
'feed': answer['feed_id'],
},
'weight': answer['weight'],
},
'region': pool_label, # the one we're answering
}
answers.append(answer)
current_pool_name = pool.data.get('fallback', None)
priority += 1
# Static/default
for answer in default_answers:
answer = {
'answer': answer['answer'],
'meta': {
'priority': priority,
'note': self._encode_notes({
'from': '--default--',
}),
'up': True,
'weight': 1,
},
'region': pool_label, # the one we're answering
}
answers.append(answer)
def _params_for_dynamic_A(self, record):
pools = record.dynamic.pools
@@ -942,6 +994,16 @@ class Ns1Provider(BaseProvider):
has_country = False
has_region = False
regions = {}
needs_shadow = False
# Get dynamic default pool name
# It is a pool that has no 'geos' associated with it
dynamic_default_pool_name = None
for i, rule in enumerate(record.dynamic.rules):
pool_name = rule.data['pool']
if not rule.data.get('geos', []):
dynamic_default_pool_name = pool_name
for i, rule in enumerate(record.dynamic.rules):
pool_name = rule.data['pool']
@@ -958,7 +1020,15 @@ class Ns1Provider(BaseProvider):
us_state = set()
for geo in rule.data.get('geos', []):
# regions dictionary below is indexed by pool names. If there
# is reuse of a pool name in the rules, the earlier one gets
# overwritten. Reuse of pool names is disallowed but for an
# exception in the case of the dynamic default pool. So, when
# the dynamic default pool name is reused elsewhere, use
# the shadow pool name for the dynamic default pool
if pool_name == dynamic_default_pool_name:
pool_name = '{}_{}'.format(pool_name, 'shadow')
needs_shadow = True
n = len(geo)
if n == 8:
# US state, e.g. NA-US-KY
@@ -1029,46 +1099,17 @@ class Ns1Provider(BaseProvider):
priority = 1
# Dynamic/health checked
current_pool_name = pool_name
seen = set()
while current_pool_name and current_pool_name not in seen:
seen.add(current_pool_name)
pool = pools[current_pool_name]
for answer in pool_answers[current_pool_name]:
answer = {
'answer': answer['answer'],
'meta': {
'priority': priority,
'note': self._encode_notes({
'from': current_pool_name,
}),
'up': {
'feed': answer['feed_id'],
},
'weight': answer['weight'],
},
'region': pool_name, # the one we're answering
}
answers.append(answer)
current_pool_name = pool.data.get('fallback', None)
priority += 1
# Static/default
for answer in default_answers:
answer = {
'answer': answer['answer'],
'meta': {
'priority': priority,
'note': self._encode_notes({
'from': '--default--',
}),
'up': True,
'weight': 1,
},
'region': pool_name, # the one we're answering
}
answers.append(answer)
self._add_answers_for_pool(answers, default_answers, pool_name,
pool_name, pool_answers, pools,
priority)
if pool_name == dynamic_default_pool_name and needs_shadow:
# If it is the dynamic default pool, add a shadow pool.
# This will be used in regions when there are rules that refer
# to the dynamic default pool
shadow_pn = '{}_{}'.format(pool_name, 'shadow')
self._add_answers_for_pool(answers, default_answers, pool_name,
shadow_pn, pool_answers, pools,
priority)
# Update filters as necessary
filters = self._get_updated_filter_chain(has_region, has_country)

View File

@@ -552,6 +552,11 @@ class TestNs1ProviderDynamic(TestCase):
'NA-US-FL'
],
'pool': 'lhr',
}, {
'geos': [
'AF-ZW',
],
'pool': 'iad',
}, {
'pool': 'iad',
}],
@@ -961,13 +966,17 @@ class TestNs1ProviderDynamic(TestCase):
]
rule0 = self.record.data['dynamic']['rules'][0]
saved_geos = rule0['geos']
rule1 = self.record.data['dynamic']['rules'][1]
rule0_saved_geos = rule0['geos']
rule1_saved_geos = rule1['geos']
rule0['geos'] = ['AF', 'EU']
rule1['geos'] = ['NA']
ret, _ = provider._params_for_A(self.record)
self.assertEquals(ret['filters'],
Ns1Provider._FILTER_CHAIN_WITH_REGION(provider,
True))
rule0['geos'] = saved_geos
rule0['geos'] = rule0_saved_geos
rule1['geos'] = rule1_saved_geos
@patch('octodns.provider.ns1.Ns1Provider._monitor_sync')
@patch('octodns.provider.ns1.Ns1Provider._monitors_for')
@@ -1123,6 +1132,21 @@ class TestNs1ProviderDynamic(TestCase):
'note': 'from:--default--',
},
'region': 'iad',
}, {
'answer': ['2.3.4.5'],
'meta': {
'priority': 1,
'weight': 12,
'note': 'from:iad_shadow',
},
'region': 'iad_shadow',
}, {
'answer': ['1.2.3.4'],
'meta': {
'priority': 2,
'note': 'from:--default--',
},
'region': 'iad_shadow',
}],
'domain': 'unit.tests',
'filters': filters,
@@ -1135,9 +1159,15 @@ class TestNs1ProviderDynamic(TestCase):
'us_state': ['OR'],
},
},
'iad': {
'iad_shadow': {
'meta': {
'note': 'rule-order:2',
'country': ['ZW'],
},
},
'iad': {
'meta': {
'note': 'rule-order:3',
},
}
},
@@ -1173,6 +1203,12 @@ class TestNs1ProviderDynamic(TestCase):
'pool': 'lhr',
}, {
'_order': '2',
'geos': [
'AF-ZW',
],
'pool': 'iad',
}, {
'_order': '3',
'pool': 'iad',
}],
},