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

minimize Azure Traffic Manager hops

This commit is contained in:
Viranch Mehta
2021-05-28 13:23:35 -07:00
parent 18c0e56759
commit aaffdb1388
2 changed files with 622 additions and 216 deletions

View File

@ -646,14 +646,23 @@ class AzureProvider(BaseProvider):
pools = defaultdict(lambda: {'fallback': None, 'values': []})
rules = []
# top level geo profile
geo_profile = self._get_tm_profile_by_id(azrecord.target_resource.id)
for geo_ep in geo_profile.endpoints:
# top level profile
root_profile = self._get_tm_profile_by_id(azrecord.target_resource.id)
if root_profile.traffic_routing_method != 'Geographic':
# This record does not use geo fencing, so we skip the Geographic
# profile hop; let's pretend to be a geo-profile's only endpoint
geo_ep = Endpoint(target_resource_id=root_profile.id)
geo_ep.target_resource = root_profile
endpoints = [geo_ep]
else:
endpoints = root_profile.endpoints
for geo_ep in endpoints:
rule = {}
# resolve list of regions
geo_map = list(geo_ep.geo_mapping)
if geo_map != ['WORLD']:
geo_map = list(geo_ep.geo_mapping or [])
if geo_map and geo_map != ['WORLD']:
if 'GEO-ME' in geo_map:
# Azure treats Middle East as a separate group, but
# its part of Asia in octoDNS, so we need to remove GEO-ME
@ -662,7 +671,7 @@ class AzureProvider(BaseProvider):
# profile was generated by octoDNS
if 'GEO-AS' not in geo_map:
msg = 'Profile={} for record {}: '.format(
geo_profile.name, azrecord.fqdn)
root_profile.name, azrecord.fqdn)
msg += 'Middle East (GEO-ME) is not supported by ' + \
'octoDNS. It needs to be either paired ' + \
'with Asia (GEO-AS) or expanded into ' + \
@ -690,18 +699,35 @@ class AzureProvider(BaseProvider):
# country
geos.append(GeoCodes.country_to_code(code))
# second level priority profile
# build fallback chain from second level priority profile
if geo_ep.target_resource_id:
target = geo_ep.target_resource
if target.traffic_routing_method == 'Priority':
rule_endpoints = target.endpoints
rule_endpoints.sort(key=lambda e: e.priority)
else:
# Weighted
geo_ep.name = target.endpoints[0].name.split('--', 1)[0]
rule_endpoints = [geo_ep]
else:
# this geo directly points to the default, so we skip the
# Priority profile hop and directly use an external endpoint;
# let's pretend to be a Priority profile's only endpoint
rule_endpoints = [geo_ep]
pool = None
rule_endpoints = geo_ep.target_resource.endpoints
rule_endpoints = sorted(rule_endpoints, key=lambda e: e.priority)
for rule_ep in rule_endpoints:
pool_name = rule_ep.name
# last/default pool
if pool_name == '--default--':
if pool_name.endswith('--default--'):
default.add(rule_ep.target)
# this should be the last one, so let's break here
break
if pool_name == '--default--':
# this should be the last one, so let's break here
break
# last pool is a single value pool and its value is same
# as record's default value
pool_name = pool_name[:-len('--default--')]
# set first priority endpoint as the rule's primary pool
if 'pool' not in rule:
@ -711,32 +737,32 @@ class AzureProvider(BaseProvider):
# set current pool as fallback of the previous pool
pool['fallback'] = pool_name
if pool_name in pools:
# we've already populated the pool
continue
# populate the pool from Weighted profile
# these should be leaf node entries with no further nesting
pool = pools[pool_name]
endpoints = []
# these should be leaf node entries with no further nesting
if rule_ep.target_resource_id:
# third (and last) level weighted RR profile
endpoints = rule_ep.target_resource.endpoints
else:
# single-value pool
# single-value pool, so we skip the Weighted profile hop
# and directly use an external endpoint; let's pretend to
# be a Weighted profile's only endpoint
endpoints = [rule_ep]
for pool_ep in endpoints:
val = pool_ep.target
value_dict = {
pool['values'].append({
'value': _check_endswith_dot(val),
'weight': pool_ep.weight or 1,
}
if value_dict not in pool['values']:
pool['values'].append(value_dict)
if 'pool' not in rule or not default:
# this will happen if the priority profile does not have
# enough endpoints
msg = 'Expected at least 2 endpoints in {}, got {}'.format(
geo_ep.target_resource.name, len(rule_endpoints)
)
raise AzureException(msg)
})
if pool_ep.name.endswith('--default--'):
default.add(val)
rules.append(rule)
@ -809,7 +835,7 @@ class AzureProvider(BaseProvider):
elif ep.target:
ep.type = endpoint_type_prefix + 'externalEndpoints'
else:
msg = ('Invalid endpoint {} in profile {}, needs to have' +
msg = ('Invalid endpoint {} in profile {}, needs to have ' +
'either target or target_resource_id').format(
ep.name, name)
raise AzureException(msg)
@ -828,39 +854,83 @@ class AzureProvider(BaseProvider):
location='global',
)
def _update_tm_name(self, profile, new_name):
profile.name = new_name
profile.id = self._profile_name_to_id(new_name)
profile.dns_config.relative_name = new_name
return profile
def _generate_traffic_managers(self, record):
traffic_managers = []
pools = record.dynamic.pools
default = record.value[:-1]
tm_suffix = _traffic_manager_suffix(record)
profile = self._generate_tm_profile
geo_endpoints = []
pool_profiles = {}
for rule in record.dynamic.rules:
# Prepare the list of Traffic manager geos
rule_geos = rule.data.get('geos', [])
geos = []
for geo in rule_geos:
if '-' in geo:
# country/state
geos.append(geo.split('-', 1)[-1])
else:
# continent
if geo == 'AS':
# Middle East is part of Asia in octoDNS, but
# Azure treats it as a separate "group", so let's
# add it in the list of geo mappings. We will drop
# it when we later parse the list of regions.
geos.append('GEO-ME')
elif geo == 'OC':
# Azure uses Australia/Pacific (AP) instead of
# Oceania
geo = 'AP'
geos.append('GEO-{}'.format(geo))
if not geos:
geos.append('WORLD')
pool_name = rule.data['pool']
rule_endpoints = []
priority = 1
default_seen = False
while pool_name:
# iterate until we reach end of fallback chain
default_seen = False
pool = pools[pool_name].data
profile_name = 'pool-{}--{}'.format(pool_name, tm_suffix)
if len(pool['values']) > 1:
# create Weighted profile for multi-value pool
endpoints = []
for val in pool['values']:
target = val['value']
# strip trailing dot from CNAME value
target = target[:-1]
endpoints.append(Endpoint(
name=target,
target=target,
weight=val.get('weight', 1),
))
pool_profile = profile(profile_name, 'Weighted', endpoints,
record)
traffic_managers.append(pool_profile)
pool_profile = pool_profiles.get(pool_name)
if pool_profile is None:
endpoints = []
for val in pool['values']:
target = val['value']
# strip trailing dot from CNAME value
target = target[:-1]
ep_name = '{}--{}'.format(pool_name, target)
if target == default:
# mark default
ep_name += '--default--'
default_seen = True
endpoints.append(Endpoint(
name=ep_name,
target=target,
weight=val.get('weight', 1),
))
profile_name = 'pool-{}--{}'.format(
pool_name, tm_suffix)
pool_profile = profile(profile_name, 'Weighted',
endpoints, record)
traffic_managers.append(pool_profile)
pool_profiles[pool_name] = pool_profile
# append pool to endpoint list of fallback rule profile
rule_endpoints.append(Endpoint(
@ -869,8 +939,15 @@ class AzureProvider(BaseProvider):
priority=priority,
))
else:
# add single-value pool as an external endpoint
# Skip Weighted profile hop for single-value pool
# append its value as an external endpoint to fallback
# rule profile
target = pool['values'][0]['value'][:-1]
ep_name = pool_name
if target == default:
# mark default
ep_name += '--default--'
default_seen = True
rule_endpoints.append(Endpoint(
name=pool_name,
target=target,
@ -880,51 +957,61 @@ class AzureProvider(BaseProvider):
priority += 1
pool_name = pool.get('fallback')
# append default profile to the end
rule_endpoints.append(Endpoint(
name='--default--',
target=record.value[:-1],
priority=priority,
))
# create rule profile with fallback chain
rule_profile_name = 'rule-{}--{}'.format(rule.data['pool'],
tm_suffix)
rule_profile = profile(rule_profile_name, 'Priority',
rule_endpoints, record)
traffic_managers.append(rule_profile)
# append default endpoint unless it is already included in
# last pool of rule profile
if not default_seen:
rule_endpoints.append(Endpoint(
name='--default--',
target=default,
priority=priority,
))
# append rule profile to top-level geo profile
rule_geos = rule.data.get('geos', [])
geos = []
if len(rule_geos) > 0:
for geo in rule_geos:
if '-' in geo:
# country or state
geos.append(geo.split('-', 1)[-1])
else:
# continent
if geo == 'AS':
# Middle East is part of Asia in octoDNS, but
# Azure treats it as a separate "group", so let's
# add it in the list of geo mappings. We will drop
# it when we later parse the list of regions.
geos.append('GEO-ME')
elif geo == 'OC':
# Azure uses Australia/Pacific (AP) instead of
# Oceania
geo = 'AP'
if len(rule_endpoints) > 1:
# create rule profile with fallback chain
rule_profile_name = 'rule-{}--{}'.format(
rule.data['pool'], tm_suffix)
rule_profile = profile(rule_profile_name, 'Priority',
rule_endpoints, record)
traffic_managers.append(rule_profile)
geos.append('GEO-{}'.format(geo))
# append rule profile to top-level geo profile
geo_endpoints.append(Endpoint(
name='rule-{}'.format(rule.data['pool']),
target_resource_id=rule_profile.id,
geo_mapping=geos,
))
else:
geos.append('WORLD')
geo_endpoints.append(Endpoint(
name='rule-{}'.format(rule.data['pool']),
target_resource_id=rule_profile.id,
geo_mapping=geos,
))
# Priority profile has only one endpoint; skip the hop and
# append its only endpoint to the top-level profile
rule_ep = rule_endpoints[0]
if rule_ep.target_resource_id:
# point directly to the Weighted pool profile
geo_endpoints.append(Endpoint(
name='rule-{}'.format(rule.data['pool']),
target_resource_id=rule_ep.target_resource_id,
geo_mapping=geos,
))
else:
# just add the value of single-value pool
geo_endpoints.append(Endpoint(
name=rule_ep.name + '--default--',
target=rule_ep.target,
geo_mapping=geos,
))
geo_profile = profile(tm_suffix, 'Geographic', geo_endpoints, record)
traffic_managers.append(geo_profile)
if len(geo_endpoints) == 1 and \
geo_endpoints[0].geo_mapping == ['WORLD'] and \
geo_endpoints[0].target_resource_id:
# Single WORLD rule does not require a Geographic profile, use
# the target profile as the root profile
target_profile_id = geo_endpoints[0].target_resource_id
profile_map = dict((tm.id, tm) for tm in traffic_managers)
target_profile = profile_map[target_profile_id]
self._update_tm_name(target_profile, tm_suffix)
else:
geo_profile = profile(tm_suffix, 'Geographic', geo_endpoints,
record)
traffic_managers.append(geo_profile)
return traffic_managers

View File

@ -614,13 +614,13 @@ class TestAzureDnsProvider(TestCase):
monitor_config=monitor,
endpoints=[
Endpoint(
name='two1.unit.tests',
name='two--two1.unit.tests',
type=external,
target='two1.unit.tests',
weight=3,
),
Endpoint(
name='two2.unit.tests',
name='two--two2.unit.tests',
type=external,
target='two2.unit.tests',
weight=4,
@ -847,121 +847,6 @@ class TestAzureDnsProvider(TestCase):
self.assertEquals(len(zone.records), 17)
self.assertTrue(exists)
def test_populate_dynamic(self):
# Middle east without Asia raises exception
provider, zone, record = self._get_dynamic_package()
tm_suffix = _traffic_manager_suffix(record)
tm_id = provider._profile_name_to_id
tm_list = provider._tm_client.profiles.list_by_resource_group
rule_name = 'rule-one--{}'.format(tm_suffix)
nested = 'Microsoft.Network/trafficManagerProfiles/nestedEndpoints'
tm_list.return_value = [
Profile(
id=tm_id(tm_suffix),
name=tm_suffix,
traffic_routing_method='Geographic',
endpoints=[
Endpoint(
geo_mapping=['GEO-ME'],
),
],
),
]
azrecord = RecordSet(
ttl=60,
target_resource=SubResource(id=tm_id(tm_suffix)),
)
azrecord.name = record.name or '@'
azrecord.type = 'Microsoft.Network/dnszones/{}'.format(record._type)
with self.assertRaises(AzureException) as ctx:
provider._populate_record(zone, azrecord)
self.assertTrue(text_type(ctx).startswith(
'Middle East (GEO-ME) is not supported'
))
# empty priority profile raises exception
provider, zone, record = self._get_dynamic_package()
tm_list = provider._tm_client.profiles.list_by_resource_group
rule_name = 'rule-one--{}'.format(tm_suffix)
nested = 'Microsoft.Network/trafficManagerProfiles/nestedEndpoints'
tm_list.return_value = [
Profile(
id=tm_id(rule_name),
name=rule_name,
traffic_routing_method='Priority',
endpoints=[],
),
Profile(
id=tm_id(tm_suffix),
name=tm_suffix,
traffic_routing_method='Geographic',
endpoints=[
Endpoint(
geo_mapping=['WORLD'],
name='rule-one',
type=nested,
target_resource_id=tm_id(rule_name),
),
],
),
]
with self.assertRaises(AzureException) as ctx:
provider._populate_record(zone, azrecord)
self.assertTrue(text_type(ctx).startswith(
'Expected at least 2 endpoints'
))
# valid set of profiles produce expected dynamic record
provider, zone, record = self._get_dynamic_package()
root_profile_id = provider._profile_name_to_id(
_traffic_manager_suffix(record)
)
azrecord = RecordSet(
ttl=60,
target_resource=SubResource(id=root_profile_id),
)
azrecord.name = record.name or '@'
azrecord.type = 'Microsoft.Network/dnszones/{}'.format(record._type)
record = provider._populate_record(zone, azrecord)
self.assertEqual(record.name, 'foo')
self.assertEqual(record.ttl, 60)
self.assertEqual(record.value, 'default.unit.tests.')
self.assertEqual(record.dynamic._data(), {
'pools': {
'one': {
'values': [
{'value': 'one.unit.tests.', 'weight': 1},
],
'fallback': 'two',
},
'two': {
'values': [
{'value': 'two1.unit.tests.', 'weight': 3},
{'value': 'two2.unit.tests.', 'weight': 4},
],
'fallback': 'three',
},
'three': {
'values': [
{'value': 'three.unit.tests.', 'weight': 1},
],
'fallback': None,
},
},
'rules': [
{'geos': ['AF', 'EU-DE', 'NA-US-CA', 'OC'], 'pool': 'one'},
{'pool': 'two'},
],
})
# valid profiles with Middle East test case
geo_profile = provider._get_tm_for_dynamic_record(record)
geo_profile.endpoints[0].geo_mapping.extend(['GEO-ME', 'GEO-AS'])
record = provider._populate_record(zone, azrecord)
self.assertIn('AS', record.dynamic.rules[0].data['geos'])
self.assertNotIn('ME', record.dynamic.rules[0].data['geos'])
def test_populate_zone(self):
provider = self._get_provider()
@ -1114,6 +999,7 @@ class TestAzureDnsProvider(TestCase):
'/providers/Microsoft.Network/trafficManagerProfiles/' + name
self.assertEqual(profile.id, expected_id)
self.assertEqual(profile.name, name)
self.assertEqual(profile.name, profile.dns_config.relative_name)
self.assertEqual(profile.traffic_routing_method, routing)
self.assertEqual(profile.dns_config.ttl, record.ttl)
self.assertEqual(len(profile.endpoints), len(endpoints))
@ -1127,33 +1013,451 @@ class TestAzureDnsProvider(TestCase):
'Microsoft.Network/trafficManagerProfiles/nestedEndpoints'
)
def test_generate_traffic_managers(self):
def test_dynamic_record(self):
provider, zone, record = self._get_dynamic_package()
profiles = provider._generate_traffic_managers(record)
deduped = []
seen = set()
for profile in profiles:
if profile.name not in seen:
deduped.append(profile)
seen.add(profile.name)
# check that every profile is a match with what we expect
expected_profiles = self._get_tm_profiles(provider)
self.assertEqual(len(expected_profiles), len(deduped))
for have, expected in zip(deduped, expected_profiles):
self.assertEqual(len(expected_profiles), len(profiles))
for have, expected in zip(profiles, expected_profiles):
self.assertTrue(_profile_is_match(have, expected))
# check that dynamic record is populated back from profiles
azrecord = RecordSet(
ttl=60,
target_resource=SubResource(id=profiles[-1].id),
)
azrecord.name = record.name or '@'
azrecord.type = 'Microsoft.Network/dnszones/{}'.format(record._type)
record2 = provider._populate_record(zone, azrecord)
self.assertEqual(record2.dynamic._data(), record.dynamic._data())
def test_generate_traffic_managers_middle_east(self):
# check Asia/Middle East test case
provider, zone, record = self._get_dynamic_package()
record.dynamic._data()['rules'][0]['geos'].append('AS')
profiles = provider._generate_traffic_managers(record)
geo_profile_name = _traffic_manager_suffix(record)
geo_profile = next(
profile
for profile in profiles
if profile.name == geo_profile_name
self.assertIn('GEO-ME', profiles[-1].endpoints[0].geo_mapping)
self.assertIn('GEO-AS', profiles[-1].endpoints[0].geo_mapping)
def test_populate_dynamic_middle_east(self):
# Middle east without Asia raises exception
provider, zone, record = self._get_dynamic_package()
tm_suffix = _traffic_manager_suffix(record)
tm_id = provider._profile_name_to_id
tm_list = provider._tm_client.profiles.list_by_resource_group
tm_list.return_value = [
Profile(
id=tm_id(tm_suffix),
name=tm_suffix,
traffic_routing_method='Geographic',
endpoints=[
Endpoint(
geo_mapping=['GEO-ME'],
),
],
),
]
azrecord = RecordSet(
ttl=60,
target_resource=SubResource(id=tm_id(tm_suffix)),
)
self.assertIn('GEO-ME', geo_profile.endpoints[0].geo_mapping)
self.assertIn('GEO-AS', geo_profile.endpoints[0].geo_mapping)
azrecord.name = record.name or '@'
azrecord.type = 'Microsoft.Network/dnszones/{}'.format(record._type)
with self.assertRaises(AzureException) as ctx:
provider._populate_record(zone, azrecord)
self.assertTrue(text_type(ctx).startswith(
'Middle East (GEO-ME) is not supported'
))
# valid profiles with Middle East test case
provider, zone, record = self._get_dynamic_package()
geo_profile = provider._get_tm_for_dynamic_record(record)
geo_profile.endpoints[0].geo_mapping.extend(['GEO-ME', 'GEO-AS'])
record = provider._populate_record(zone, azrecord)
self.assertIn('AS', record.dynamic.rules[0].data['geos'])
self.assertNotIn('ME', record.dynamic.rules[0].data['geos'])
def test_dynamic_no_geo(self):
# test that traffic managers are generated as expected
provider, zone, record = self._get_dynamic_package()
external = 'Microsoft.Network/trafficManagerProfiles/externalEndpoints'
record = Record.new(zone, 'foo', data={
'type': 'CNAME',
'ttl': 60,
'value': 'default.unit.tests.',
'dynamic': {
'pools': {
'one': {
'values': [
{'value': 'one.unit.tests.'},
],
},
},
'rules': [
{'pool': 'one'},
],
}
})
profiles = provider._generate_traffic_managers(record)
self.assertEqual(len(profiles), 1)
self.assertTrue(_profile_is_match(profiles[0], Profile(
name='foo-unit-tests',
traffic_routing_method='Priority',
dns_config=DnsConfig(
relative_name='foo-unit-tests', ttl=60),
monitor_config=_get_monitor(record),
endpoints=[
Endpoint(
name='one',
type=external,
target='one.unit.tests',
priority=1,
),
Endpoint(
name='--default--',
type=external,
target='default.unit.tests',
priority=2,
),
],
)))
# test that same record gets populated back from traffic managers
tm_list = provider._tm_client.profiles.list_by_resource_group
tm_list.return_value = profiles
azrecord = RecordSet(
ttl=60,
target_resource=SubResource(id=profiles[0].id),
)
azrecord.name = record.name or '@'
azrecord.type = 'Microsoft.Network/dnszones/{}'.format(record._type)
record2 = provider._populate_record(zone, azrecord)
self.assertEqual(record2.dynamic._data(), record.dynamic._data())
def test_dynamic_fallback_is_default(self):
# test that traffic managers are generated as expected
provider, zone, record = self._get_dynamic_package()
external = 'Microsoft.Network/trafficManagerProfiles/externalEndpoints'
record = Record.new(zone, 'foo', data={
'type': 'CNAME',
'ttl': 60,
'value': 'default.unit.tests.',
'dynamic': {
'pools': {
'def': {
'values': [
{'value': 'default.unit.tests.'},
],
},
},
'rules': [
{'geos': ['AF'], 'pool': 'def'},
],
}
})
profiles = provider._generate_traffic_managers(record)
self.assertEqual(len(profiles), 1)
self.assertTrue(_profile_is_match(profiles[0], Profile(
name='foo-unit-tests',
traffic_routing_method='Geographic',
dns_config=DnsConfig(
relative_name='foo-unit-tests', ttl=60),
monitor_config=_get_monitor(record),
endpoints=[
Endpoint(
name='def--default--',
type=external,
target='default.unit.tests',
geo_mapping=['GEO-AF'],
),
],
)))
# test that same record gets populated back from traffic managers
tm_list = provider._tm_client.profiles.list_by_resource_group
tm_list.return_value = profiles
azrecord = RecordSet(
ttl=60,
target_resource=SubResource(id=profiles[0].id),
)
azrecord.name = record.name or '@'
azrecord.type = 'Microsoft.Network/dnszones/{}'.format(record._type)
record2 = provider._populate_record(zone, azrecord)
self.assertEqual(record2.dynamic._data(), record.dynamic._data())
def test_dynamic_pool_contains_default(self):
# test that traffic managers are generated as expected
provider, zone, record = self._get_dynamic_package()
tm_id = provider._profile_name_to_id
external = 'Microsoft.Network/trafficManagerProfiles/externalEndpoints'
nested = 'Microsoft.Network/trafficManagerProfiles/nestedEndpoints'
record = Record.new(zone, 'foo', data={
'type': 'CNAME',
'ttl': 60,
'value': 'default.unit.tests.',
'dynamic': {
'pools': {
'rr': {
'values': [
{'value': 'one.unit.tests.'},
{'value': 'two.unit.tests.'},
{'value': 'default.unit.tests.'},
{'value': 'final.unit.tests.'},
],
},
},
'rules': [
{'geos': ['AF'], 'pool': 'rr'},
],
}
})
profiles = provider._generate_traffic_managers(record)
self.assertEqual(len(profiles), 2)
self.assertTrue(_profile_is_match(profiles[0], Profile(
name='pool-rr--foo-unit-tests',
traffic_routing_method='Weighted',
dns_config=DnsConfig(
relative_name='pool-rr--foo-unit-tests', ttl=60),
monitor_config=_get_monitor(record),
endpoints=[
Endpoint(
name='rr--one.unit.tests',
type=external,
target='one.unit.tests',
weight=1,
),
Endpoint(
name='rr--two.unit.tests',
type=external,
target='two.unit.tests',
weight=1,
),
Endpoint(
name='rr--default.unit.tests--default--',
type=external,
target='default.unit.tests',
weight=1,
),
Endpoint(
name='rr--final.unit.tests',
type=external,
target='final.unit.tests',
weight=1,
),
],
)))
self.assertTrue(_profile_is_match(profiles[1], Profile(
name='foo-unit-tests',
traffic_routing_method='Geographic',
dns_config=DnsConfig(
relative_name='foo-unit-tests', ttl=60),
monitor_config=_get_monitor(record),
endpoints=[
Endpoint(
name='rule-rr',
type=nested,
target_resource_id=tm_id('pool-rr--foo-unit-tests'),
geo_mapping=['GEO-AF'],
),
],
)))
# test that same record gets populated back from traffic managers
tm_list = provider._tm_client.profiles.list_by_resource_group
tm_list.return_value = profiles
azrecord = RecordSet(
ttl=60,
target_resource=SubResource(id=profiles[1].id),
)
azrecord.name = record.name or '@'
azrecord.type = 'Microsoft.Network/dnszones/{}'.format(record._type)
record2 = provider._populate_record(zone, azrecord)
self.assertEqual(record2.dynamic._data(), record.dynamic._data())
def test_dynamic_pool_contains_default_no_geo(self):
# test that traffic managers are generated as expected
provider, zone, record = self._get_dynamic_package()
external = 'Microsoft.Network/trafficManagerProfiles/externalEndpoints'
record = Record.new(zone, 'foo', data={
'type': 'CNAME',
'ttl': 60,
'value': 'default.unit.tests.',
'dynamic': {
'pools': {
'rr': {
'values': [
{'value': 'one.unit.tests.'},
{'value': 'two.unit.tests.'},
{'value': 'default.unit.tests.'},
{'value': 'final.unit.tests.'},
],
},
},
'rules': [
{'pool': 'rr'},
],
}
})
profiles = provider._generate_traffic_managers(record)
self.assertEqual(len(profiles), 1)
self.assertTrue(_profile_is_match(profiles[0], Profile(
name='foo-unit-tests',
traffic_routing_method='Weighted',
dns_config=DnsConfig(
relative_name='foo-unit-tests', ttl=60),
monitor_config=_get_monitor(record),
endpoints=[
Endpoint(
name='rr--one.unit.tests',
type=external,
target='one.unit.tests',
weight=1,
),
Endpoint(
name='rr--two.unit.tests',
type=external,
target='two.unit.tests',
weight=1,
),
Endpoint(
name='rr--default.unit.tests--default--',
type=external,
target='default.unit.tests',
weight=1,
),
Endpoint(
name='rr--final.unit.tests',
type=external,
target='final.unit.tests',
weight=1,
),
],
)))
# test that same record gets populated back from traffic managers
tm_list = provider._tm_client.profiles.list_by_resource_group
tm_list.return_value = profiles
azrecord = RecordSet(
ttl=60,
target_resource=SubResource(id=profiles[0].id),
)
azrecord.name = record.name or '@'
azrecord.type = 'Microsoft.Network/dnszones/{}'.format(record._type)
record2 = provider._populate_record(zone, azrecord)
self.assertEqual(record2.dynamic._data(), record.dynamic._data())
def test_dynamic_last_pool_contains_default_no_geo(self):
# test that traffic managers are generated as expected
provider, zone, record = self._get_dynamic_package()
tm_id = provider._profile_name_to_id
external = 'Microsoft.Network/trafficManagerProfiles/externalEndpoints'
nested = 'Microsoft.Network/trafficManagerProfiles/nestedEndpoints'
record = Record.new(zone, 'foo', data={
'type': 'CNAME',
'ttl': 60,
'value': 'default.unit.tests.',
'dynamic': {
'pools': {
'cloud': {
'values': [
{'value': 'cloud.unit.tests.'},
],
'fallback': 'rr',
},
'rr': {
'values': [
{'value': 'one.unit.tests.'},
{'value': 'two.unit.tests.'},
{'value': 'default.unit.tests.'},
{'value': 'final.unit.tests.'},
],
},
},
'rules': [
{'pool': 'cloud'},
],
}
})
profiles = provider._generate_traffic_managers(record)
self.assertEqual(len(profiles), 2)
self.assertTrue(_profile_is_match(profiles[0], Profile(
name='pool-rr--foo-unit-tests',
traffic_routing_method='Weighted',
dns_config=DnsConfig(
relative_name='pool-rr--foo-unit-tests', ttl=60),
monitor_config=_get_monitor(record),
endpoints=[
Endpoint(
name='rr--one.unit.tests',
type=external,
target='one.unit.tests',
weight=1,
),
Endpoint(
name='rr--two.unit.tests',
type=external,
target='two.unit.tests',
weight=1,
),
Endpoint(
name='rr--default.unit.tests--default--',
type=external,
target='default.unit.tests',
weight=1,
),
Endpoint(
name='rr--final.unit.tests',
type=external,
target='final.unit.tests',
weight=1,
),
],
)))
self.assertTrue(_profile_is_match(profiles[1], Profile(
name='foo-unit-tests',
traffic_routing_method='Priority',
dns_config=DnsConfig(
relative_name='foo-unit-tests', ttl=60),
monitor_config=_get_monitor(record),
endpoints=[
Endpoint(
name='cloud',
type=external,
target='cloud.unit.tests',
priority=1,
),
Endpoint(
name='rr',
type=nested,
target_resource_id=tm_id('pool-rr--foo-unit-tests'),
priority=2,
),
],
)))
# test that same record gets populated back from traffic managers
tm_list = provider._tm_client.profiles.list_by_resource_group
tm_list.return_value = profiles
azrecord = RecordSet(
ttl=60,
target_resource=SubResource(id=profiles[1].id),
)
azrecord.name = record.name or '@'
azrecord.type = 'Microsoft.Network/dnszones/{}'.format(record._type)
record2 = provider._populate_record(zone, azrecord)
self.assertEqual(record2.dynamic._data(), record.dynamic._data())
def test_sync_traffic_managers(self):
provider, zone, record = self._get_dynamic_package()
@ -1194,6 +1498,21 @@ class TestAzureDnsProvider(TestCase):
)
self.assertEqual(new_profile.endpoints[0].weight, 14)
@patch(
'octodns.provider.azuredns.AzureProvider._generate_traffic_managers')
def test_sync_traffic_managers_duplicate(self, mock_gen_tms):
provider, zone, record = self._get_dynamic_package()
tm_sync = provider._tm_client.profiles.create_or_update
# change and duplicate profiles
profile = self._get_tm_profiles(provider)[0]
profile.name = 'changing_this_to_trigger_sync'
mock_gen_tms.return_value = [profile, profile]
provider._sync_traffic_managers(record)
# it should only be called once for duplicate profiles
tm_sync.assert_called_once()
def test_find_traffic_managers(self):
provider, zone, record = self._get_dynamic_package()