mirror of
https://github.com/github/octodns.git
synced 2024-05-11 05:55:00 +00:00
Merge pull request #723 from viranch/azuredns-optimize
minimize Azure Traffic Manager hops for dynamic records
This commit is contained in:
@@ -281,12 +281,29 @@ def _profile_is_match(have, desired):
|
|||||||
if have is None or desired is None:
|
if have is None or desired is None:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
log = logging.getLogger('azuredns._profile_is_match').debug
|
||||||
|
|
||||||
|
def false(have, desired, name=None):
|
||||||
|
prefix = 'profile={}'.format(name) if name else ''
|
||||||
|
attr = have.__class__.__name__
|
||||||
|
log('%s have.%s = %s', prefix, attr, have)
|
||||||
|
log('%s desired.%s = %s', prefix, attr, desired)
|
||||||
|
return False
|
||||||
|
|
||||||
# compare basic attributes
|
# compare basic attributes
|
||||||
if have.name != desired.name or \
|
if have.name != desired.name or \
|
||||||
have.traffic_routing_method != desired.traffic_routing_method or \
|
have.traffic_routing_method != desired.traffic_routing_method or \
|
||||||
have.dns_config.ttl != desired.dns_config.ttl or \
|
|
||||||
len(have.endpoints) != len(desired.endpoints):
|
len(have.endpoints) != len(desired.endpoints):
|
||||||
return False
|
return false(have, desired)
|
||||||
|
|
||||||
|
# compare dns config
|
||||||
|
dns_have = have.dns_config
|
||||||
|
dns_desired = desired.dns_config
|
||||||
|
if dns_have.ttl != dns_desired.ttl or \
|
||||||
|
dns_have.relative_name is None or \
|
||||||
|
dns_desired.relative_name is None or \
|
||||||
|
dns_have.relative_name != dns_desired.relative_name:
|
||||||
|
return false(dns_have, dns_desired, have.name)
|
||||||
|
|
||||||
# compare monitoring configuration
|
# compare monitoring configuration
|
||||||
monitor_have = have.monitor_config
|
monitor_have = have.monitor_config
|
||||||
@@ -295,7 +312,7 @@ def _profile_is_match(have, desired):
|
|||||||
monitor_have.port != monitor_desired.port or \
|
monitor_have.port != monitor_desired.port or \
|
||||||
monitor_have.path != monitor_desired.path or \
|
monitor_have.path != monitor_desired.path or \
|
||||||
monitor_have.custom_headers != monitor_desired.custom_headers:
|
monitor_have.custom_headers != monitor_desired.custom_headers:
|
||||||
return False
|
return false(monitor_have, monitor_desired, have.name)
|
||||||
|
|
||||||
# compare endpoints
|
# compare endpoints
|
||||||
method = have.traffic_routing_method
|
method = have.traffic_routing_method
|
||||||
@@ -313,26 +330,26 @@ def _profile_is_match(have, desired):
|
|||||||
for have_endpoint, desired_endpoint in endpoints:
|
for have_endpoint, desired_endpoint in endpoints:
|
||||||
if have_endpoint.name != desired_endpoint.name or \
|
if have_endpoint.name != desired_endpoint.name or \
|
||||||
have_endpoint.type != desired_endpoint.type:
|
have_endpoint.type != desired_endpoint.type:
|
||||||
return False
|
return false(have_endpoint, desired_endpoint, have.name)
|
||||||
target_type = have_endpoint.type.split('/')[-1]
|
target_type = have_endpoint.type.split('/')[-1]
|
||||||
if target_type == 'externalEndpoints':
|
if target_type == 'externalEndpoints':
|
||||||
# compare value, weight, priority
|
# compare value, weight, priority
|
||||||
if have_endpoint.target != desired_endpoint.target:
|
if have_endpoint.target != desired_endpoint.target:
|
||||||
return False
|
return false(have_endpoint, desired_endpoint, have.name)
|
||||||
if method == 'Weighted' and \
|
if method == 'Weighted' and \
|
||||||
have_endpoint.weight != desired_endpoint.weight:
|
have_endpoint.weight != desired_endpoint.weight:
|
||||||
return False
|
return false(have_endpoint, desired_endpoint, have.name)
|
||||||
elif target_type == 'nestedEndpoints':
|
elif target_type == 'nestedEndpoints':
|
||||||
# compare targets
|
# compare targets
|
||||||
if have_endpoint.target_resource_id != \
|
if have_endpoint.target_resource_id != \
|
||||||
desired_endpoint.target_resource_id:
|
desired_endpoint.target_resource_id:
|
||||||
return False
|
return false(have_endpoint, desired_endpoint, have.name)
|
||||||
# compare geos
|
# compare geos
|
||||||
if method == 'Geographic':
|
if method == 'Geographic':
|
||||||
have_geos = sorted(have_endpoint.geo_mapping)
|
have_geos = sorted(have_endpoint.geo_mapping)
|
||||||
desired_geos = sorted(desired_endpoint.geo_mapping)
|
desired_geos = sorted(desired_endpoint.geo_mapping)
|
||||||
if have_geos != desired_geos:
|
if have_geos != desired_geos:
|
||||||
return False
|
return false(have_endpoint, desired_endpoint, have.name)
|
||||||
else:
|
else:
|
||||||
# unexpected, give up
|
# unexpected, give up
|
||||||
return False
|
return False
|
||||||
@@ -629,14 +646,23 @@ class AzureProvider(BaseProvider):
|
|||||||
pools = defaultdict(lambda: {'fallback': None, 'values': []})
|
pools = defaultdict(lambda: {'fallback': None, 'values': []})
|
||||||
rules = []
|
rules = []
|
||||||
|
|
||||||
# top level geo profile
|
# top level profile
|
||||||
geo_profile = self._get_tm_profile_by_id(azrecord.target_resource.id)
|
root_profile = self._get_tm_profile_by_id(azrecord.target_resource.id)
|
||||||
for geo_ep in geo_profile.endpoints:
|
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 = {}
|
rule = {}
|
||||||
|
|
||||||
# resolve list of regions
|
# resolve list of regions
|
||||||
geo_map = list(geo_ep.geo_mapping)
|
geo_map = list(geo_ep.geo_mapping or [])
|
||||||
if geo_map != ['WORLD']:
|
if geo_map and geo_map != ['WORLD']:
|
||||||
if 'GEO-ME' in geo_map:
|
if 'GEO-ME' in geo_map:
|
||||||
# Azure treats Middle East as a separate group, but
|
# Azure treats Middle East as a separate group, but
|
||||||
# its part of Asia in octoDNS, so we need to remove GEO-ME
|
# its part of Asia in octoDNS, so we need to remove GEO-ME
|
||||||
@@ -645,7 +671,7 @@ class AzureProvider(BaseProvider):
|
|||||||
# profile was generated by octoDNS
|
# profile was generated by octoDNS
|
||||||
if 'GEO-AS' not in geo_map:
|
if 'GEO-AS' not in geo_map:
|
||||||
msg = 'Profile={} for record {}: '.format(
|
msg = 'Profile={} for record {}: '.format(
|
||||||
geo_profile.name, azrecord.fqdn)
|
root_profile.name, azrecord.fqdn)
|
||||||
msg += 'Middle East (GEO-ME) is not supported by ' + \
|
msg += 'Middle East (GEO-ME) is not supported by ' + \
|
||||||
'octoDNS. It needs to be either paired ' + \
|
'octoDNS. It needs to be either paired ' + \
|
||||||
'with Asia (GEO-AS) or expanded into ' + \
|
'with Asia (GEO-AS) or expanded into ' + \
|
||||||
@@ -673,18 +699,35 @@ class AzureProvider(BaseProvider):
|
|||||||
# country
|
# country
|
||||||
geos.append(GeoCodes.country_to_code(code))
|
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
|
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:
|
for rule_ep in rule_endpoints:
|
||||||
pool_name = rule_ep.name
|
pool_name = rule_ep.name
|
||||||
|
|
||||||
# last/default pool
|
# last/default pool
|
||||||
if pool_name == '--default--':
|
if pool_name.endswith('--default--'):
|
||||||
default.add(rule_ep.target)
|
default.add(rule_ep.target)
|
||||||
# this should be the last one, so let's break here
|
if pool_name == '--default--':
|
||||||
break
|
# 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
|
# set first priority endpoint as the rule's primary pool
|
||||||
if 'pool' not in rule:
|
if 'pool' not in rule:
|
||||||
@@ -694,32 +737,32 @@ class AzureProvider(BaseProvider):
|
|||||||
# set current pool as fallback of the previous pool
|
# set current pool as fallback of the previous pool
|
||||||
pool['fallback'] = pool_name
|
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]
|
pool = pools[pool_name]
|
||||||
endpoints = []
|
endpoints = []
|
||||||
# these should be leaf node entries with no further nesting
|
|
||||||
if rule_ep.target_resource_id:
|
if rule_ep.target_resource_id:
|
||||||
# third (and last) level weighted RR profile
|
# third (and last) level weighted RR profile
|
||||||
endpoints = rule_ep.target_resource.endpoints
|
endpoints = rule_ep.target_resource.endpoints
|
||||||
else:
|
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]
|
endpoints = [rule_ep]
|
||||||
|
|
||||||
for pool_ep in endpoints:
|
for pool_ep in endpoints:
|
||||||
val = pool_ep.target
|
val = pool_ep.target
|
||||||
value_dict = {
|
pool['values'].append({
|
||||||
'value': _check_endswith_dot(val),
|
'value': _check_endswith_dot(val),
|
||||||
'weight': pool_ep.weight or 1,
|
'weight': pool_ep.weight or 1,
|
||||||
}
|
})
|
||||||
if value_dict not in pool['values']:
|
if pool_ep.name.endswith('--default--'):
|
||||||
pool['values'].append(value_dict)
|
default.add(val)
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
rules.append(rule)
|
rules.append(rule)
|
||||||
|
|
||||||
@@ -792,7 +835,7 @@ class AzureProvider(BaseProvider):
|
|||||||
elif ep.target:
|
elif ep.target:
|
||||||
ep.type = endpoint_type_prefix + 'externalEndpoints'
|
ep.type = endpoint_type_prefix + 'externalEndpoints'
|
||||||
else:
|
else:
|
||||||
msg = ('Invalid endpoint {} in profile {}, needs to have' +
|
msg = ('Invalid endpoint {} in profile {}, needs to have ' +
|
||||||
'either target or target_resource_id').format(
|
'either target or target_resource_id').format(
|
||||||
ep.name, name)
|
ep.name, name)
|
||||||
raise AzureException(msg)
|
raise AzureException(msg)
|
||||||
@@ -811,39 +854,83 @@ class AzureProvider(BaseProvider):
|
|||||||
location='global',
|
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):
|
def _generate_traffic_managers(self, record):
|
||||||
traffic_managers = []
|
traffic_managers = []
|
||||||
pools = record.dynamic.pools
|
pools = record.dynamic.pools
|
||||||
|
|
||||||
|
default = record.value[:-1]
|
||||||
tm_suffix = _traffic_manager_suffix(record)
|
tm_suffix = _traffic_manager_suffix(record)
|
||||||
profile = self._generate_tm_profile
|
profile = self._generate_tm_profile
|
||||||
|
|
||||||
geo_endpoints = []
|
geo_endpoints = []
|
||||||
|
pool_profiles = {}
|
||||||
|
|
||||||
for rule in record.dynamic.rules:
|
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']
|
pool_name = rule.data['pool']
|
||||||
rule_endpoints = []
|
rule_endpoints = []
|
||||||
priority = 1
|
priority = 1
|
||||||
|
default_seen = False
|
||||||
|
|
||||||
while pool_name:
|
while pool_name:
|
||||||
# iterate until we reach end of fallback chain
|
# iterate until we reach end of fallback chain
|
||||||
|
default_seen = False
|
||||||
pool = pools[pool_name].data
|
pool = pools[pool_name].data
|
||||||
profile_name = 'pool-{}--{}'.format(pool_name, tm_suffix)
|
|
||||||
if len(pool['values']) > 1:
|
if len(pool['values']) > 1:
|
||||||
# create Weighted profile for multi-value pool
|
# create Weighted profile for multi-value pool
|
||||||
endpoints = []
|
pool_profile = pool_profiles.get(pool_name)
|
||||||
for val in pool['values']:
|
if pool_profile is None:
|
||||||
target = val['value']
|
endpoints = []
|
||||||
# strip trailing dot from CNAME value
|
for val in pool['values']:
|
||||||
target = target[:-1]
|
target = val['value']
|
||||||
endpoints.append(Endpoint(
|
# strip trailing dot from CNAME value
|
||||||
name=target,
|
target = target[:-1]
|
||||||
target=target,
|
ep_name = '{}--{}'.format(pool_name, target)
|
||||||
weight=val.get('weight', 1),
|
if target == default:
|
||||||
))
|
# mark default
|
||||||
pool_profile = profile(profile_name, 'Weighted', endpoints,
|
ep_name += '--default--'
|
||||||
record)
|
default_seen = True
|
||||||
traffic_managers.append(pool_profile)
|
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
|
# append pool to endpoint list of fallback rule profile
|
||||||
rule_endpoints.append(Endpoint(
|
rule_endpoints.append(Endpoint(
|
||||||
@@ -852,8 +939,15 @@ class AzureProvider(BaseProvider):
|
|||||||
priority=priority,
|
priority=priority,
|
||||||
))
|
))
|
||||||
else:
|
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]
|
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(
|
rule_endpoints.append(Endpoint(
|
||||||
name=pool_name,
|
name=pool_name,
|
||||||
target=target,
|
target=target,
|
||||||
@@ -863,51 +957,61 @@ class AzureProvider(BaseProvider):
|
|||||||
priority += 1
|
priority += 1
|
||||||
pool_name = pool.get('fallback')
|
pool_name = pool.get('fallback')
|
||||||
|
|
||||||
# append default profile to the end
|
# append default endpoint unless it is already included in
|
||||||
rule_endpoints.append(Endpoint(
|
# last pool of rule profile
|
||||||
name='--default--',
|
if not default_seen:
|
||||||
target=record.value[:-1],
|
rule_endpoints.append(Endpoint(
|
||||||
priority=priority,
|
name='--default--',
|
||||||
))
|
target=default,
|
||||||
# create rule profile with fallback chain
|
priority=priority,
|
||||||
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 rule profile to top-level geo profile
|
if len(rule_endpoints) > 1:
|
||||||
rule_geos = rule.data.get('geos', [])
|
# create rule profile with fallback chain
|
||||||
geos = []
|
rule_profile_name = 'rule-{}--{}'.format(
|
||||||
if len(rule_geos) > 0:
|
rule.data['pool'], tm_suffix)
|
||||||
for geo in rule_geos:
|
rule_profile = profile(rule_profile_name, 'Priority',
|
||||||
if '-' in geo:
|
rule_endpoints, record)
|
||||||
# country or state
|
traffic_managers.append(rule_profile)
|
||||||
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))
|
# 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:
|
else:
|
||||||
geos.append('WORLD')
|
# Priority profile has only one endpoint; skip the hop and
|
||||||
geo_endpoints.append(Endpoint(
|
# append its only endpoint to the top-level profile
|
||||||
name='rule-{}'.format(rule.data['pool']),
|
rule_ep = rule_endpoints[0]
|
||||||
target_resource_id=rule_profile.id,
|
if rule_ep.target_resource_id:
|
||||||
geo_mapping=geos,
|
# 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)
|
if len(geo_endpoints) == 1 and \
|
||||||
traffic_managers.append(geo_profile)
|
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
|
return traffic_managers
|
||||||
|
|
||||||
|
|||||||
@@ -453,6 +453,7 @@ class Test_ProfileIsMatch(TestCase):
|
|||||||
name = 'foo-unit-tests',
|
name = 'foo-unit-tests',
|
||||||
ttl = 60,
|
ttl = 60,
|
||||||
method = 'Geographic',
|
method = 'Geographic',
|
||||||
|
dns_name = None,
|
||||||
monitor_proto = 'HTTPS',
|
monitor_proto = 'HTTPS',
|
||||||
monitor_port = 4443,
|
monitor_port = 4443,
|
||||||
monitor_path = '/_ping',
|
monitor_path = '/_ping',
|
||||||
@@ -465,7 +466,7 @@ class Test_ProfileIsMatch(TestCase):
|
|||||||
weight = 1,
|
weight = 1,
|
||||||
priority = 1,
|
priority = 1,
|
||||||
):
|
):
|
||||||
dns = DnsConfig(ttl=ttl)
|
dns = DnsConfig(relative_name=(dns_name or name), ttl=ttl)
|
||||||
return Profile(
|
return Profile(
|
||||||
name=name, traffic_routing_method=method, dns_config=dns,
|
name=name, traffic_routing_method=method, dns_config=dns,
|
||||||
monitor_config=MonitorConfig(
|
monitor_config=MonitorConfig(
|
||||||
@@ -488,6 +489,7 @@ class Test_ProfileIsMatch(TestCase):
|
|||||||
|
|
||||||
self.assertFalse(is_match(profile(), profile(name='two')))
|
self.assertFalse(is_match(profile(), profile(name='two')))
|
||||||
self.assertFalse(is_match(profile(), profile(endpoints=2)))
|
self.assertFalse(is_match(profile(), profile(endpoints=2)))
|
||||||
|
self.assertFalse(is_match(profile(), profile(dns_name='two')))
|
||||||
self.assertFalse(is_match(profile(), profile(monitor_proto='HTTP')))
|
self.assertFalse(is_match(profile(), profile(monitor_proto='HTTP')))
|
||||||
self.assertFalse(is_match(profile(), profile(endpoint_name='a')))
|
self.assertFalse(is_match(profile(), profile(endpoint_name='a')))
|
||||||
self.assertFalse(is_match(profile(), profile(endpoint_type='b')))
|
self.assertFalse(is_match(profile(), profile(endpoint_type='b')))
|
||||||
@@ -596,7 +598,6 @@ class TestAzureDnsProvider(TestCase):
|
|||||||
id_format = base_id + '{}--' + suffix
|
id_format = base_id + '{}--' + suffix
|
||||||
name_format = '{}--' + suffix
|
name_format = '{}--' + suffix
|
||||||
|
|
||||||
dns = DnsConfig(ttl=60)
|
|
||||||
header = MonitorConfigCustomHeadersItem(name='Host',
|
header = MonitorConfigCustomHeadersItem(name='Host',
|
||||||
value='foo.unit.tests')
|
value='foo.unit.tests')
|
||||||
monitor = MonitorConfig(protocol='HTTPS', port=4443, path='/_ping',
|
monitor = MonitorConfig(protocol='HTTPS', port=4443, path='/_ping',
|
||||||
@@ -604,22 +605,22 @@ class TestAzureDnsProvider(TestCase):
|
|||||||
external = 'Microsoft.Network/trafficManagerProfiles/externalEndpoints'
|
external = 'Microsoft.Network/trafficManagerProfiles/externalEndpoints'
|
||||||
nested = 'Microsoft.Network/trafficManagerProfiles/nestedEndpoints'
|
nested = 'Microsoft.Network/trafficManagerProfiles/nestedEndpoints'
|
||||||
|
|
||||||
return [
|
profiles = [
|
||||||
Profile(
|
Profile(
|
||||||
id=id_format.format('pool-two'),
|
id=id_format.format('pool-two'),
|
||||||
name=name_format.format('pool-two'),
|
name=name_format.format('pool-two'),
|
||||||
traffic_routing_method='Weighted',
|
traffic_routing_method='Weighted',
|
||||||
dns_config=dns,
|
dns_config=DnsConfig(ttl=60),
|
||||||
monitor_config=monitor,
|
monitor_config=monitor,
|
||||||
endpoints=[
|
endpoints=[
|
||||||
Endpoint(
|
Endpoint(
|
||||||
name='two1.unit.tests',
|
name='two--two1.unit.tests',
|
||||||
type=external,
|
type=external,
|
||||||
target='two1.unit.tests',
|
target='two1.unit.tests',
|
||||||
weight=3,
|
weight=3,
|
||||||
),
|
),
|
||||||
Endpoint(
|
Endpoint(
|
||||||
name='two2.unit.tests',
|
name='two--two2.unit.tests',
|
||||||
type=external,
|
type=external,
|
||||||
target='two2.unit.tests',
|
target='two2.unit.tests',
|
||||||
weight=4,
|
weight=4,
|
||||||
@@ -630,7 +631,7 @@ class TestAzureDnsProvider(TestCase):
|
|||||||
id=id_format.format('rule-one'),
|
id=id_format.format('rule-one'),
|
||||||
name=name_format.format('rule-one'),
|
name=name_format.format('rule-one'),
|
||||||
traffic_routing_method='Priority',
|
traffic_routing_method='Priority',
|
||||||
dns_config=dns,
|
dns_config=DnsConfig(ttl=60),
|
||||||
monitor_config=monitor,
|
monitor_config=monitor,
|
||||||
endpoints=[
|
endpoints=[
|
||||||
Endpoint(
|
Endpoint(
|
||||||
@@ -663,7 +664,7 @@ class TestAzureDnsProvider(TestCase):
|
|||||||
id=id_format.format('rule-two'),
|
id=id_format.format('rule-two'),
|
||||||
name=name_format.format('rule-two'),
|
name=name_format.format('rule-two'),
|
||||||
traffic_routing_method='Priority',
|
traffic_routing_method='Priority',
|
||||||
dns_config=dns,
|
dns_config=DnsConfig(ttl=60),
|
||||||
monitor_config=monitor,
|
monitor_config=monitor,
|
||||||
endpoints=[
|
endpoints=[
|
||||||
Endpoint(
|
Endpoint(
|
||||||
@@ -690,7 +691,7 @@ class TestAzureDnsProvider(TestCase):
|
|||||||
id=base_id + suffix,
|
id=base_id + suffix,
|
||||||
name=suffix,
|
name=suffix,
|
||||||
traffic_routing_method='Geographic',
|
traffic_routing_method='Geographic',
|
||||||
dns_config=dns,
|
dns_config=DnsConfig(ttl=60),
|
||||||
monitor_config=monitor,
|
monitor_config=monitor,
|
||||||
endpoints=[
|
endpoints=[
|
||||||
Endpoint(
|
Endpoint(
|
||||||
@@ -709,6 +710,11 @@ class TestAzureDnsProvider(TestCase):
|
|||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
for profile in profiles:
|
||||||
|
profile.dns_config.relative_name = profile.name
|
||||||
|
|
||||||
|
return profiles
|
||||||
|
|
||||||
def _get_dynamic_package(self):
|
def _get_dynamic_package(self):
|
||||||
'''Convenience function to setup a sample dynamic record.
|
'''Convenience function to setup a sample dynamic record.
|
||||||
'''
|
'''
|
||||||
@@ -841,121 +847,6 @@ class TestAzureDnsProvider(TestCase):
|
|||||||
self.assertEquals(len(zone.records), 17)
|
self.assertEquals(len(zone.records), 17)
|
||||||
self.assertTrue(exists)
|
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):
|
def test_populate_zone(self):
|
||||||
provider = self._get_provider()
|
provider = self._get_provider()
|
||||||
|
|
||||||
@@ -1108,6 +999,7 @@ class TestAzureDnsProvider(TestCase):
|
|||||||
'/providers/Microsoft.Network/trafficManagerProfiles/' + name
|
'/providers/Microsoft.Network/trafficManagerProfiles/' + name
|
||||||
self.assertEqual(profile.id, expected_id)
|
self.assertEqual(profile.id, expected_id)
|
||||||
self.assertEqual(profile.name, name)
|
self.assertEqual(profile.name, name)
|
||||||
|
self.assertEqual(profile.name, profile.dns_config.relative_name)
|
||||||
self.assertEqual(profile.traffic_routing_method, routing)
|
self.assertEqual(profile.traffic_routing_method, routing)
|
||||||
self.assertEqual(profile.dns_config.ttl, record.ttl)
|
self.assertEqual(profile.dns_config.ttl, record.ttl)
|
||||||
self.assertEqual(len(profile.endpoints), len(endpoints))
|
self.assertEqual(len(profile.endpoints), len(endpoints))
|
||||||
@@ -1121,33 +1013,451 @@ class TestAzureDnsProvider(TestCase):
|
|||||||
'Microsoft.Network/trafficManagerProfiles/nestedEndpoints'
|
'Microsoft.Network/trafficManagerProfiles/nestedEndpoints'
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_generate_traffic_managers(self):
|
def test_dynamic_record(self):
|
||||||
provider, zone, record = self._get_dynamic_package()
|
provider, zone, record = self._get_dynamic_package()
|
||||||
profiles = provider._generate_traffic_managers(record)
|
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
|
# check that every profile is a match with what we expect
|
||||||
expected_profiles = self._get_tm_profiles(provider)
|
expected_profiles = self._get_tm_profiles(provider)
|
||||||
self.assertEqual(len(expected_profiles), len(deduped))
|
self.assertEqual(len(expected_profiles), len(profiles))
|
||||||
for have, expected in zip(deduped, expected_profiles):
|
for have, expected in zip(profiles, expected_profiles):
|
||||||
self.assertTrue(_profile_is_match(have, expected))
|
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
|
# check Asia/Middle East test case
|
||||||
|
provider, zone, record = self._get_dynamic_package()
|
||||||
record.dynamic._data()['rules'][0]['geos'].append('AS')
|
record.dynamic._data()['rules'][0]['geos'].append('AS')
|
||||||
profiles = provider._generate_traffic_managers(record)
|
profiles = provider._generate_traffic_managers(record)
|
||||||
geo_profile_name = _traffic_manager_suffix(record)
|
self.assertIn('GEO-ME', profiles[-1].endpoints[0].geo_mapping)
|
||||||
geo_profile = next(
|
self.assertIn('GEO-AS', profiles[-1].endpoints[0].geo_mapping)
|
||||||
profile
|
|
||||||
for profile in profiles
|
def test_populate_dynamic_middle_east(self):
|
||||||
if profile.name == geo_profile_name
|
# 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)
|
azrecord.name = record.name or '@'
|
||||||
self.assertIn('GEO-AS', geo_profile.endpoints[0].geo_mapping)
|
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):
|
def test_sync_traffic_managers(self):
|
||||||
provider, zone, record = self._get_dynamic_package()
|
provider, zone, record = self._get_dynamic_package()
|
||||||
@@ -1188,6 +1498,21 @@ class TestAzureDnsProvider(TestCase):
|
|||||||
)
|
)
|
||||||
self.assertEqual(new_profile.endpoints[0].weight, 14)
|
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):
|
def test_find_traffic_managers(self):
|
||||||
provider, zone, record = self._get_dynamic_package()
|
provider, zone, record = self._get_dynamic_package()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user