diff --git a/octodns/provider/azuredns.py b/octodns/provider/azuredns.py index ef9affd..f590776 100644 --- a/octodns/provider/azuredns.py +++ b/octodns/provider/azuredns.py @@ -1114,7 +1114,8 @@ class AzureProvider(BaseProvider): else: # Skip Weighted profile hop for single-value pool; append its # value as an external endpoint to fallback rule profile - target = pool_values[0]['value'] + value = pool_values[0] + target = value['value'] if record._type == 'CNAME': target = target[:-1] ep_name = pool_name @@ -1122,10 +1123,13 @@ class AzureProvider(BaseProvider): # mark default ep_name += '--default--' default_seen = True + ep_status = 'Disabled' if value['status'] == 'down' else \ + 'Enabled' return Endpoint( name=ep_name, target=target, priority=priority, + endpoint_status=ep_status, ), default_seen def _make_rule_profile(self, rule_endpoints, rule_name, record, geos, diff --git a/tests/test_octodns_provider_azuredns.py b/tests/test_octodns_provider_azuredns.py index 72369a2..b531ad2 100644 --- a/tests/test_octodns_provider_azuredns.py +++ b/tests/test_octodns_provider_azuredns.py @@ -1717,6 +1717,80 @@ class TestAzureDnsProvider(TestCase): changes = provider._extra_changes(zone, desired, []) self.assertEqual(len(changes), 0) + def test_dynamic_pool_status(self): + # test that traffic managers are generated as expected for pool value + # statuses + provider = self._get_provider() + zone1 = Zone('unit.tests.', []) + record1 = Record.new(zone1, 'foo', data={ + 'type': 'CNAME', + 'ttl': 60, + 'value': 'default.unit.tests.', + 'dynamic': { + 'pools': { + 'one': { + 'values': [ + {'value': 'one1.unit.tests.', 'status': 'up'}, + ], + }, + 'two': { + 'values': [ + {'value': 'two1.unit.tests.', 'status': 'down'}, + {'value': 'two2.unit.tests.'}, + ], + }, + }, + 'rules': [ + {'geos': ['AS'], 'pool': 'one'}, + {'pool': 'two'}, + ], + } + }) + zone1.add_record(record1) + zone2 = provider._process_desired_zone(zone1.copy()) + record2 = list(zone2.records)[0] + self.assertTrue( + record2.dynamic.pools['one'].data['values'][0]['status'], + 'obey' + ) + + record1.dynamic.pools['one'].data['values'][0]['status'] = 'down' + profiles = provider._generate_traffic_managers(record1) + self.assertEqual(len(profiles), 4) + self.assertEqual(profiles[0].endpoints[0].endpoint_status, 'Disabled') + self.assertEqual(profiles[1].endpoints[0].endpoint_status, 'Disabled') + + # # 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 = record1.name or '@' + azrecord.type = f'Microsoft.Network/dnszones/{record1._type}' + record2 = provider._populate_record(zone, azrecord) + self.assertEqual(record1.dynamic._data(), record2.dynamic._data()) + + # _process_desired_zone shouldn't change anything when not needed + zone1 = Zone(zone.name, sub_zones=[]) + zone1.add_record(record1) + zone2 = provider._process_desired_zone(zone1.copy()) + record2 = list(zone2.records)[0] + self.assertTrue(record1.data, record2.data) + + # simple records should not get changed by _process_desired_zone + zone1 = Zone(zone.name, sub_zones=[]) + record1 = Record.new(zone1, 'foo', data={ + 'type': 'CNAME', + 'ttl': 86400, + 'value': 'one.unit.tests.', + }) + zone1.add_record(record1) + zone2 = provider._process_desired_zone(zone1.copy()) + record2 = list(zone2.records)[0] + self.assertTrue(record1.data, record2.data) + def test_dynamic_A(self): provider = self._get_provider() external = 'Microsoft.Network/trafficManagerProfiles/externalEndpoints' diff --git a/tests/test_octodns_provider_base.py b/tests/test_octodns_provider_base.py index 7fd60c5..a5b4e10 100644 --- a/tests/test_octodns_provider_base.py +++ b/tests/test_octodns_provider_base.py @@ -293,6 +293,19 @@ class TestBaseProvider(TestCase): record2 = list(zone2.records)[0] self.assertTrue(record2.dynamic) + # SUPPORTS_POOL_VALUE_STATUS + provider.SUPPORTS_POOL_VALUE_STATUS = False + zone1 = Zone('unit.tests.', []) + record1.dynamic.pools['one'].data['values'][0]['status'] = 'up' + zone1.add_record(record1) + + zone2 = provider._process_desired_zone(zone1.copy()) + record2 = list(zone2.records)[0] + self.assertEqual( + record2.dynamic.pools['one'].data['values'][0]['status'], + 'obey' + ) + def test_safe_none(self): # No changes is safe Plan(None, None, [], True).raise_if_unsafe() diff --git a/tests/test_octodns_provider_ns1.py b/tests/test_octodns_provider_ns1.py index 6cd6a36..ffa9181 100644 --- a/tests/test_octodns_provider_ns1.py +++ b/tests/test_octodns_provider_ns1.py @@ -1209,6 +1209,34 @@ class TestNs1ProviderDynamic(TestCase): monitors_delete_mock.assert_has_calls([call('mon-id2')]) notifylists_delete_mock.assert_not_called() + @patch('octodns.provider.ns1.Ns1Provider._monitors_for') + def test_params_for_dynamic_with_pool_status(self, monitors_for_mock): + provider = Ns1Provider('test', 'api-key') + monitors_for_mock.reset_mock() + monitors_for_mock.side_effect = [{}] + record = Record.new(self.zone, '', { + 'dynamic': { + 'pools': { + 'iad': { + 'values': [{ + 'value': '1.2.3.4', + 'status': 'up', + }], + }, + }, + 'rules': [{ + 'pool': 'iad', + }], + }, + 'ttl': 32, + 'type': 'A', + 'value': '1.2.3.4', + 'meta': {}, + }) + params, active_monitors = provider._params_for_dynamic(record) + self.assertTrue(params['answers'][0]['meta']['up']) + self.assertEqual(len(active_monitors), 0) + @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, @@ -1802,7 +1830,7 @@ class TestNs1ProviderDynamic(TestCase): 'meta': { 'priority': 1, 'note': 'from:one__country pool:one fallback:two', - 'up': {}, + 'up': True, }, 'region': 'one_country', }, { @@ -1880,7 +1908,9 @@ class TestNs1ProviderDynamic(TestCase): }, 'one': { 'fallback': 'two', - 'values': [{'value': '1.1.1.1', 'weight': 1}] + 'values': [ + {'value': '1.1.1.1', 'weight': 1, 'status': 'up'}, + ], }, 'three': { 'fallback': None, diff --git a/tests/test_octodns_record.py b/tests/test_octodns_record.py index 60d71ff..68d3d61 100644 --- a/tests/test_octodns_record.py +++ b/tests/test_octodns_record.py @@ -4543,6 +4543,29 @@ class TestDynamicRecords(TestCase): # This should be valid, no exception Record.new(self.zone, 'bad', a_data) + # invalid status + a_data = { + 'dynamic': { + 'pools': { + 'one': { + 'values': [{ + 'value': '2.2.2.2', + 'status': 'none', + }], + }, + }, + 'rules': [{ + 'pool': 'one', + }], + }, + 'ttl': 60, + 'type': 'A', + 'values': ['1.1.1.1'], + } + with self.assertRaises(ValidationError) as ctx: + Record.new(self.zone, 'bad', a_data) + self.assertIn('invalid status', ctx.exception.reasons[0]) + def test_dynamic_lenient(self): # Missing pools a_data = {