diff --git a/octodns/manager.py b/octodns/manager.py index dcbf083..104e445 100644 --- a/octodns/manager.py +++ b/octodns/manager.py @@ -261,7 +261,8 @@ class Manager(object): try: source.populate(zone, lenient=lenient) except TypeError as e: - if "keyword argument 'lenient'" not in text_type(e): + if ("unexpected keyword argument 'lenient'" + not in text_type(e)): raise self.log.warn('provider %s does not accept lenient ' 'param', source.__class__.__name__) diff --git a/octodns/provider/azuredns.py b/octodns/provider/azuredns.py index 4551c6c..87bbef0 100644 --- a/octodns/provider/azuredns.py +++ b/octodns/provider/azuredns.py @@ -837,8 +837,8 @@ class AzureProvider(BaseProvider): pool['fallback'] = pool_name if pool_name in pools: - # we've already populated the pool - continue + # we've already populated this and subsequent pools + break # populate the pool from Weighted profile # these should be leaf node entries with no further nesting @@ -922,6 +922,16 @@ class AzureProvider(BaseProvider): for profile in profiles: name = profile.name + + endpoints = set() + for ep in profile.endpoints: + if not ep.target: + continue + if ep.target in endpoints: + msg = '{} contains duplicate endpoint {}' + raise AzureException(msg.format(name, ep.target)) + endpoints.add(ep.target) + if name in seen_profiles: # exit if a possible collision is detected, even though # we've tried to ensure unique mapping @@ -1053,7 +1063,6 @@ class AzureProvider(BaseProvider): while pool_name: # iterate until we reach end of fallback chain - default_seen = False pool = pools[pool_name].data if len(pool['values']) > 1: # create Weighted profile for multi-value pool diff --git a/tests/test_octodns_provider_azuredns.py b/tests/test_octodns_provider_azuredns.py index eb74007..e09de28 100644 --- a/tests/test_octodns_provider_azuredns.py +++ b/tests/test_octodns_provider_azuredns.py @@ -999,6 +999,51 @@ class TestAzureDnsProvider(TestCase): 'Collision in Traffic Manager' )) + @patch( + 'octodns.provider.azuredns.AzureProvider._generate_traffic_managers') + def test_extra_changes_non_last_fallback_contains_default(self, mock_gtm): + provider = self._get_provider() + + desired = Zone(zone.name, sub_zones=[]) + record = Record.new(desired, 'foo', { + 'type': 'CNAME', + 'ttl': 60, + 'value': 'default.unit.tests.', + 'dynamic': { + 'pools': { + 'one': { + 'values': [{'value': 'one.unit.tests.'}], + 'fallback': 'def', + }, + 'def': { + 'values': [{'value': 'default.unit.tests.'}], + 'fallback': 'two', + }, + 'two': { + 'values': [{'value': 'two.unit.tests.'}], + }, + }, + 'rules': [ + {'pool': 'one'}, + ] + } + }) + desired.add_record(record) + changes = [Create(record)] + + # assert that no exception is raised + provider._extra_changes(zone, desired, changes) + + # simulate duplicate endpoint and assert exception + endpoint = Endpoint(target='dup.unit.tests.') + mock_gtm.return_value = [Profile( + name='test-profile', + endpoints=[endpoint, endpoint], + )] + with self.assertRaises(AzureException) as ctx: + provider._extra_changes(zone, desired, changes) + self.assertTrue('duplicate endpoint' in text_type(ctx)) + def test_extra_changes_invalid_dynamic_A(self): provider = self._get_provider() @@ -1642,6 +1687,12 @@ class TestAzureDnsProvider(TestCase): 'value': 'default.unit.tests.', 'dynamic': { 'pools': { + 'sto': { + 'values': [ + {'value': 'sto.unit.tests.'}, + ], + 'fallback': 'iad', + }, 'iad': { 'values': [ {'value': 'iad.unit.tests.'}, @@ -1657,13 +1708,14 @@ class TestAzureDnsProvider(TestCase): 'rules': [ {'geos': ['EU'], 'pool': 'iad'}, {'geos': ['EU-GB'], 'pool': 'lhr'}, + {'geos': ['EU-SE'], 'pool': 'sto'}, {'pool': 'lhr'}, ], } }) profiles = provider._generate_traffic_managers(record) - self.assertEqual(len(profiles), 3) + self.assertEqual(len(profiles), 4) self.assertTrue(_profile_is_match(profiles[-1], Profile( name='foo--unit--tests', traffic_routing_method='Geographic', @@ -1683,6 +1735,12 @@ class TestAzureDnsProvider(TestCase): target_resource_id=profiles[1].id, geo_mapping=['GB', 'WORLD'], ), + Endpoint( + name='rule-sto', + type=nested, + target_resource_id=profiles[2].id, + geo_mapping=['SE'], + ), ], ))) diff --git a/tests/test_octodns_provider_ultra.py b/tests/test_octodns_provider_ultra.py index 52e0307..a22a489 100644 --- a/tests/test_octodns_provider_ultra.py +++ b/tests/test_octodns_provider_ultra.py @@ -1,8 +1,11 @@ +from __future__ import unicode_literals + from mock import Mock, call from os.path import dirname, join from requests import HTTPError from requests_mock import ANY, mock as requests_mock from six import text_type +from six.moves.urllib import parse from unittest import TestCase from json import load as json_load @@ -55,7 +58,8 @@ class TestUltraProvider(TestCase): self.assertEquals(1, mock.call_count) expected_payload = "grant_type=password&username=user&"\ "password=rightpass" - self.assertEquals(mock.last_request.text, expected_payload) + self.assertEquals(parse.parse_qs(mock.last_request.text), + parse.parse_qs(expected_payload)) def test_get_zones(self): provider = _get_provider() diff --git a/tests/test_octodns_source_envvar.py b/tests/test_octodns_source_envvar.py index 0714883..ac66a22 100644 --- a/tests/test_octodns_source_envvar.py +++ b/tests/test_octodns_source_envvar.py @@ -1,6 +1,6 @@ +from mock import patch from six import text_type from unittest import TestCase -from unittest.mock import patch from octodns.source.envvar import EnvVarSource from octodns.source.envvar import EnvironmentVariableNotFoundException