mirror of
https://github.com/github/octodns.git
synced 2024-05-11 05:55:00 +00:00
Implement black formatting
This commit is contained in:
+6
-6
@@ -2,8 +2,12 @@
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
from shutil import rmtree
|
||||
from tempfile import mkdtemp
|
||||
@@ -15,7 +19,6 @@ from octodns.provider.yaml import YamlProvider
|
||||
|
||||
|
||||
class SimpleSource(object):
|
||||
|
||||
def __init__(self, id='test'):
|
||||
pass
|
||||
|
||||
@@ -78,13 +81,11 @@ class DynamicProvider(object):
|
||||
|
||||
|
||||
class NoSshFpProvider(SimpleProvider):
|
||||
|
||||
def supports(self, record):
|
||||
return record._type != 'SSHFP'
|
||||
|
||||
|
||||
class TemporaryDirectory(object):
|
||||
|
||||
def __init__(self, delete_on_exit=True):
|
||||
self.delete_on_exit = delete_on_exit
|
||||
|
||||
@@ -100,7 +101,6 @@ class TemporaryDirectory(object):
|
||||
|
||||
|
||||
class WantsConfigProcessor(BaseProcessor):
|
||||
|
||||
def __init__(self, name, some_config):
|
||||
super(WantsConfigProcessor, self).__init__(name)
|
||||
|
||||
|
||||
@@ -2,8 +2,12 @@
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
@@ -11,11 +15,8 @@ from octodns.equality import EqualityTupleMixin
|
||||
|
||||
|
||||
class TestEqualityTupleMixin(TestCase):
|
||||
|
||||
def test_basics(self):
|
||||
|
||||
class Simple(EqualityTupleMixin):
|
||||
|
||||
def __init__(self, a, b, c):
|
||||
self.a = a
|
||||
self.b = b
|
||||
@@ -60,7 +61,6 @@ class TestEqualityTupleMixin(TestCase):
|
||||
self.assertTrue(one >= same)
|
||||
|
||||
def test_not_implemented(self):
|
||||
|
||||
class MissingMethod(EqualityTupleMixin):
|
||||
pass
|
||||
|
||||
|
||||
+189
-142
@@ -2,15 +2,23 @@
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
from os import environ
|
||||
from os.path import dirname, join
|
||||
|
||||
from octodns import __VERSION__
|
||||
from octodns.manager import _AggregateTarget, MainThreadExecutor, Manager, \
|
||||
ManagerException
|
||||
from octodns.manager import (
|
||||
_AggregateTarget,
|
||||
MainThreadExecutor,
|
||||
Manager,
|
||||
ManagerException,
|
||||
)
|
||||
from octodns.processor.base import BaseProcessor
|
||||
from octodns.record import Create, Delete, Record
|
||||
from octodns.yaml import safe_load
|
||||
@@ -19,8 +27,14 @@ from octodns.zone import Zone
|
||||
from unittest import TestCase
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from helpers import DynamicProvider, GeoProvider, NoSshFpProvider, \
|
||||
PlannableProvider, SimpleProvider, TemporaryDirectory
|
||||
from helpers import (
|
||||
DynamicProvider,
|
||||
GeoProvider,
|
||||
NoSshFpProvider,
|
||||
PlannableProvider,
|
||||
SimpleProvider,
|
||||
TemporaryDirectory,
|
||||
)
|
||||
|
||||
config_dir = join(dirname(__file__), 'config')
|
||||
|
||||
@@ -30,7 +44,6 @@ def get_config_filename(which):
|
||||
|
||||
|
||||
class TestManager(TestCase):
|
||||
|
||||
def test_missing_provider_class(self):
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
Manager(get_config_filename('missing-provider-class.yaml')).sync()
|
||||
@@ -43,14 +56,16 @@ class TestManager(TestCase):
|
||||
|
||||
def test_bad_provider_class_module(self):
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
Manager(get_config_filename('bad-provider-class-module.yaml')) \
|
||||
.sync()
|
||||
Manager(
|
||||
get_config_filename('bad-provider-class-module.yaml')
|
||||
).sync()
|
||||
self.assertTrue('Unknown provider class' in str(ctx.exception))
|
||||
|
||||
def test_bad_provider_class_no_module(self):
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
Manager(get_config_filename('bad-provider-class-no-module.yaml')) \
|
||||
.sync()
|
||||
Manager(
|
||||
get_config_filename('bad-provider-class-no-module.yaml')
|
||||
).sync()
|
||||
self.assertTrue('Unknown provider class' in str(ctx.exception))
|
||||
|
||||
def test_missing_provider_config(self):
|
||||
@@ -66,141 +81,160 @@ class TestManager(TestCase):
|
||||
|
||||
def test_missing_source(self):
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
Manager(get_config_filename('provider-problems.yaml')) \
|
||||
.sync(['missing.sources.'])
|
||||
Manager(get_config_filename('provider-problems.yaml')).sync(
|
||||
['missing.sources.']
|
||||
)
|
||||
self.assertTrue('missing sources' in str(ctx.exception))
|
||||
|
||||
def test_missing_targets(self):
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
Manager(get_config_filename('provider-problems.yaml')) \
|
||||
.sync(['missing.targets.'])
|
||||
Manager(get_config_filename('provider-problems.yaml')).sync(
|
||||
['missing.targets.']
|
||||
)
|
||||
self.assertTrue('missing targets' in str(ctx.exception))
|
||||
|
||||
def test_unknown_source(self):
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
Manager(get_config_filename('provider-problems.yaml')) \
|
||||
.sync(['unknown.source.'])
|
||||
Manager(get_config_filename('provider-problems.yaml')).sync(
|
||||
['unknown.source.']
|
||||
)
|
||||
self.assertTrue('unknown source' in str(ctx.exception))
|
||||
|
||||
def test_unknown_target(self):
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
Manager(get_config_filename('provider-problems.yaml')) \
|
||||
.sync(['unknown.target.'])
|
||||
Manager(get_config_filename('provider-problems.yaml')).sync(
|
||||
['unknown.target.']
|
||||
)
|
||||
self.assertTrue('unknown target' in str(ctx.exception))
|
||||
|
||||
def test_bad_plan_output_class(self):
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
name = 'bad-plan-output-missing-class.yaml'
|
||||
Manager(get_config_filename(name)).sync()
|
||||
self.assertEqual('plan_output bad is missing class',
|
||||
str(ctx.exception))
|
||||
self.assertEqual('plan_output bad is missing class', str(ctx.exception))
|
||||
|
||||
def test_bad_plan_output_config(self):
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
Manager(get_config_filename('bad-plan-output-config.yaml')).sync()
|
||||
self.assertEqual('Incorrect plan_output config for bad',
|
||||
str(ctx.exception))
|
||||
self.assertEqual(
|
||||
'Incorrect plan_output config for bad', str(ctx.exception)
|
||||
)
|
||||
|
||||
def test_source_only_as_a_target(self):
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
Manager(get_config_filename('provider-problems.yaml')) \
|
||||
.sync(['not.targetable.'])
|
||||
self.assertTrue('does not support targeting' in
|
||||
str(ctx.exception))
|
||||
Manager(get_config_filename('provider-problems.yaml')).sync(
|
||||
['not.targetable.']
|
||||
)
|
||||
self.assertTrue('does not support targeting' in str(ctx.exception))
|
||||
|
||||
def test_always_dry_run(self):
|
||||
with TemporaryDirectory() as tmpdir:
|
||||
environ['YAML_TMP_DIR'] = tmpdir.dirname
|
||||
tc = Manager(get_config_filename('always-dry-run.yaml')) \
|
||||
.sync(dry_run=False)
|
||||
tc = Manager(get_config_filename('always-dry-run.yaml')).sync(
|
||||
dry_run=False
|
||||
)
|
||||
# only the stuff from subzone, unit.tests. is always-dry-run
|
||||
self.assertEqual(3, tc)
|
||||
|
||||
def test_simple(self):
|
||||
with TemporaryDirectory() as tmpdir:
|
||||
environ['YAML_TMP_DIR'] = tmpdir.dirname
|
||||
tc = Manager(get_config_filename('simple.yaml')) \
|
||||
.sync(dry_run=False)
|
||||
tc = Manager(get_config_filename('simple.yaml')).sync(dry_run=False)
|
||||
self.assertEqual(28, tc)
|
||||
|
||||
# try with just one of the zones
|
||||
tc = Manager(get_config_filename('simple.yaml')) \
|
||||
.sync(dry_run=False, eligible_zones=['unit.tests.'])
|
||||
tc = Manager(get_config_filename('simple.yaml')).sync(
|
||||
dry_run=False, eligible_zones=['unit.tests.']
|
||||
)
|
||||
self.assertEqual(22, tc)
|
||||
|
||||
# the subzone, with 2 targets
|
||||
tc = Manager(get_config_filename('simple.yaml')) \
|
||||
.sync(dry_run=False, eligible_zones=['subzone.unit.tests.'])
|
||||
tc = Manager(get_config_filename('simple.yaml')).sync(
|
||||
dry_run=False, eligible_zones=['subzone.unit.tests.']
|
||||
)
|
||||
self.assertEqual(6, tc)
|
||||
|
||||
# and finally the empty zone
|
||||
tc = Manager(get_config_filename('simple.yaml')) \
|
||||
.sync(dry_run=False, eligible_zones=['empty.'])
|
||||
tc = Manager(get_config_filename('simple.yaml')).sync(
|
||||
dry_run=False, eligible_zones=['empty.']
|
||||
)
|
||||
self.assertEqual(0, tc)
|
||||
|
||||
# Again with force
|
||||
tc = Manager(get_config_filename('simple.yaml')) \
|
||||
.sync(dry_run=False, force=True)
|
||||
tc = Manager(get_config_filename('simple.yaml')).sync(
|
||||
dry_run=False, force=True
|
||||
)
|
||||
self.assertEqual(28, tc)
|
||||
|
||||
# Again with max_workers = 1
|
||||
tc = Manager(get_config_filename('simple.yaml'), max_workers=1) \
|
||||
.sync(dry_run=False, force=True)
|
||||
tc = Manager(
|
||||
get_config_filename('simple.yaml'), max_workers=1
|
||||
).sync(dry_run=False, force=True)
|
||||
self.assertEqual(28, tc)
|
||||
|
||||
# Include meta
|
||||
tc = Manager(get_config_filename('simple.yaml'), max_workers=1,
|
||||
include_meta=True) \
|
||||
.sync(dry_run=False, force=True)
|
||||
tc = Manager(
|
||||
get_config_filename('simple.yaml'),
|
||||
max_workers=1,
|
||||
include_meta=True,
|
||||
).sync(dry_run=False, force=True)
|
||||
self.assertEqual(33, tc)
|
||||
|
||||
def test_eligible_sources(self):
|
||||
with TemporaryDirectory() as tmpdir:
|
||||
environ['YAML_TMP_DIR'] = tmpdir.dirname
|
||||
# Only allow a target that doesn't exist
|
||||
tc = Manager(get_config_filename('simple.yaml')) \
|
||||
.sync(eligible_sources=['foo'])
|
||||
tc = Manager(get_config_filename('simple.yaml')).sync(
|
||||
eligible_sources=['foo']
|
||||
)
|
||||
self.assertEqual(0, tc)
|
||||
|
||||
def test_eligible_targets(self):
|
||||
with TemporaryDirectory() as tmpdir:
|
||||
environ['YAML_TMP_DIR'] = tmpdir.dirname
|
||||
# Only allow a target that doesn't exist
|
||||
tc = Manager(get_config_filename('simple.yaml')) \
|
||||
.sync(eligible_targets=['foo'])
|
||||
tc = Manager(get_config_filename('simple.yaml')).sync(
|
||||
eligible_targets=['foo']
|
||||
)
|
||||
self.assertEqual(0, tc)
|
||||
|
||||
def test_aliases(self):
|
||||
with TemporaryDirectory() as tmpdir:
|
||||
environ['YAML_TMP_DIR'] = tmpdir.dirname
|
||||
# Alias zones with a valid target.
|
||||
tc = Manager(get_config_filename('simple-alias-zone.yaml')) \
|
||||
.sync()
|
||||
tc = Manager(get_config_filename('simple-alias-zone.yaml')).sync()
|
||||
self.assertEqual(0, tc)
|
||||
|
||||
# Alias zone with an invalid target.
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
tc = Manager(get_config_filename('unknown-source-zone.yaml')) \
|
||||
.sync()
|
||||
self.assertEqual('Invalid alias zone alias.tests.: source zone '
|
||||
'does-not-exists.tests. does not exist',
|
||||
str(ctx.exception))
|
||||
tc = Manager(
|
||||
get_config_filename('unknown-source-zone.yaml')
|
||||
).sync()
|
||||
self.assertEqual(
|
||||
'Invalid alias zone alias.tests.: source zone '
|
||||
'does-not-exists.tests. does not exist',
|
||||
str(ctx.exception),
|
||||
)
|
||||
|
||||
# Alias zone that points to another alias zone.
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
tc = Manager(get_config_filename('alias-zone-loop.yaml')) \
|
||||
.sync()
|
||||
self.assertEqual('Invalid alias zone alias-loop.tests.: source '
|
||||
'zone alias.tests. is an alias zone',
|
||||
str(ctx.exception))
|
||||
tc = Manager(get_config_filename('alias-zone-loop.yaml')).sync()
|
||||
self.assertEqual(
|
||||
'Invalid alias zone alias-loop.tests.: source '
|
||||
'zone alias.tests. is an alias zone',
|
||||
str(ctx.exception),
|
||||
)
|
||||
|
||||
# Sync an alias without the zone it refers to
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
tc = Manager(get_config_filename('simple-alias-zone.yaml')) \
|
||||
.sync(eligible_zones=["alias.tests."])
|
||||
self.assertEqual('Zone alias.tests. cannot be sync without zone '
|
||||
'unit.tests. sinced it is aliased',
|
||||
str(ctx.exception))
|
||||
tc = Manager(
|
||||
get_config_filename('simple-alias-zone.yaml')
|
||||
).sync(eligible_zones=["alias.tests."])
|
||||
self.assertEqual(
|
||||
'Zone alias.tests. cannot be sync without zone '
|
||||
'unit.tests. sinced it is aliased',
|
||||
str(ctx.exception),
|
||||
)
|
||||
|
||||
def test_compare(self):
|
||||
with TemporaryDirectory() as tmpdir:
|
||||
@@ -223,9 +257,9 @@ class TestManager(TestCase):
|
||||
self.assertEqual(23, len(changes))
|
||||
|
||||
# Compound sources with varying support
|
||||
changes = manager.compare(['in', 'nosshfp'],
|
||||
['dump'],
|
||||
'unit.tests.')
|
||||
changes = manager.compare(
|
||||
['in', 'nosshfp'], ['dump'], 'unit.tests.'
|
||||
)
|
||||
self.assertEqual(22, len(changes))
|
||||
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
@@ -249,8 +283,9 @@ class TestManager(TestCase):
|
||||
# exception
|
||||
with self.assertRaises(AttributeError) as ctx:
|
||||
at.FOO
|
||||
self.assertEqual('_AggregateTarget object has no attribute FOO',
|
||||
str(ctx.exception))
|
||||
self.assertEqual(
|
||||
'_AggregateTarget object has no attribute FOO', str(ctx.exception)
|
||||
)
|
||||
|
||||
self.assertFalse(_AggregateTarget([simple, simple]).SUPPORTS_GEO)
|
||||
self.assertFalse(_AggregateTarget([simple, geo]).SUPPORTS_GEO)
|
||||
@@ -263,15 +298,19 @@ class TestManager(TestCase):
|
||||
self.assertTrue(_AggregateTarget([dynamic, dynamic]).SUPPORTS_DYNAMIC)
|
||||
|
||||
zone = Zone('unit.tests.', [])
|
||||
record = Record.new(zone, 'sshfp', {
|
||||
'ttl': 60,
|
||||
'type': 'SSHFP',
|
||||
'value': {
|
||||
'algorithm': 1,
|
||||
'fingerprint_type': 1,
|
||||
'fingerprint': 'abcdefg',
|
||||
record = Record.new(
|
||||
zone,
|
||||
'sshfp',
|
||||
{
|
||||
'ttl': 60,
|
||||
'type': 'SSHFP',
|
||||
'value': {
|
||||
'algorithm': 1,
|
||||
'fingerprint_type': 1,
|
||||
'fingerprint': 'abcdefg',
|
||||
},
|
||||
},
|
||||
})
|
||||
)
|
||||
self.assertTrue(simple.supports(record))
|
||||
self.assertFalse(nosshfp.supports(record))
|
||||
self.assertTrue(_AggregateTarget([simple, simple]).supports(record))
|
||||
@@ -283,8 +322,9 @@ class TestManager(TestCase):
|
||||
manager = Manager(get_config_filename('simple.yaml'))
|
||||
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
manager.dump('unit.tests.', tmpdir.dirname, False, False,
|
||||
'nope')
|
||||
manager.dump(
|
||||
'unit.tests.', tmpdir.dirname, False, False, 'nope'
|
||||
)
|
||||
self.assertEqual('Unknown source: nope', str(ctx.exception))
|
||||
|
||||
manager.dump('unit.tests.', tmpdir.dirname, False, False, 'in')
|
||||
@@ -292,8 +332,9 @@ class TestManager(TestCase):
|
||||
# make sure this fails with an IOError and not a KeyError when
|
||||
# trying to find sub zones
|
||||
with self.assertRaises(IOError):
|
||||
manager.dump('unknown.zone.', tmpdir.dirname, False, False,
|
||||
'in')
|
||||
manager.dump(
|
||||
'unknown.zone.', tmpdir.dirname, False, False, 'in'
|
||||
)
|
||||
|
||||
def test_dump_empty(self):
|
||||
with TemporaryDirectory() as tmpdir:
|
||||
@@ -312,8 +353,7 @@ class TestManager(TestCase):
|
||||
manager = Manager(get_config_filename('simple-split.yaml'))
|
||||
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
manager.dump('unit.tests.', tmpdir.dirname, False, True,
|
||||
'nope')
|
||||
manager.dump('unit.tests.', tmpdir.dirname, False, True, 'nope')
|
||||
self.assertEqual('Unknown source: nope', str(ctx.exception))
|
||||
|
||||
manager.dump('unit.tests.', tmpdir.dirname, False, True, 'in')
|
||||
@@ -321,41 +361,46 @@ class TestManager(TestCase):
|
||||
# make sure this fails with an OSError and not a KeyError when
|
||||
# trying to find sub zones
|
||||
with self.assertRaises(OSError):
|
||||
manager.dump('unknown.zone.', tmpdir.dirname, False, True,
|
||||
'in')
|
||||
manager.dump('unknown.zone.', tmpdir.dirname, False, True, 'in')
|
||||
|
||||
def test_validate_configs(self):
|
||||
Manager(get_config_filename('simple-validate.yaml')).validate_configs()
|
||||
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
Manager(get_config_filename('missing-sources.yaml')) \
|
||||
.validate_configs()
|
||||
Manager(
|
||||
get_config_filename('missing-sources.yaml')
|
||||
).validate_configs()
|
||||
self.assertTrue('missing sources' in str(ctx.exception))
|
||||
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
Manager(get_config_filename('unknown-provider.yaml')) \
|
||||
.validate_configs()
|
||||
Manager(
|
||||
get_config_filename('unknown-provider.yaml')
|
||||
).validate_configs()
|
||||
self.assertTrue('unknown source' in str(ctx.exception))
|
||||
|
||||
# Alias zone using an invalid source zone.
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
Manager(get_config_filename('unknown-source-zone.yaml')) \
|
||||
.validate_configs()
|
||||
Manager(
|
||||
get_config_filename('unknown-source-zone.yaml')
|
||||
).validate_configs()
|
||||
self.assertTrue('does not exist' in str(ctx.exception))
|
||||
|
||||
# Alias zone that points to another alias zone.
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
Manager(get_config_filename('alias-zone-loop.yaml')) \
|
||||
.validate_configs()
|
||||
Manager(
|
||||
get_config_filename('alias-zone-loop.yaml')
|
||||
).validate_configs()
|
||||
self.assertTrue('is an alias zone' in str(ctx.exception))
|
||||
|
||||
# Valid config file using an alias zone.
|
||||
Manager(get_config_filename('simple-alias-zone.yaml')) \
|
||||
.validate_configs()
|
||||
Manager(
|
||||
get_config_filename('simple-alias-zone.yaml')
|
||||
).validate_configs()
|
||||
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
Manager(get_config_filename('unknown-processor.yaml')) \
|
||||
.validate_configs()
|
||||
Manager(
|
||||
get_config_filename('unknown-processor.yaml')
|
||||
).validate_configs()
|
||||
self.assertTrue('unknown processor' in str(ctx.exception))
|
||||
|
||||
def test_get_zone(self):
|
||||
@@ -366,8 +411,9 @@ class TestManager(TestCase):
|
||||
self.assertTrue('missing ending dot' in str(ctx.exception))
|
||||
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
Manager(get_config_filename('simple.yaml')) \
|
||||
.get_zone('unknown-zone.tests.')
|
||||
Manager(get_config_filename('simple.yaml')).get_zone(
|
||||
'unknown-zone.tests.'
|
||||
)
|
||||
self.assertTrue('Unknown zone name' in str(ctx.exception))
|
||||
|
||||
def test_populate_lenient_fallback(self):
|
||||
@@ -377,7 +423,6 @@ class TestManager(TestCase):
|
||||
manager = Manager(get_config_filename('simple.yaml'))
|
||||
|
||||
class NoLenient(SimpleProvider):
|
||||
|
||||
def populate(self, zone):
|
||||
pass
|
||||
|
||||
@@ -385,14 +430,12 @@ class TestManager(TestCase):
|
||||
manager._populate_and_plan('unit.tests.', [], [NoLenient()], [])
|
||||
|
||||
class OtherType(SimpleProvider):
|
||||
|
||||
def populate(self, zone, lenient=False):
|
||||
raise TypeError('something else')
|
||||
|
||||
# This will blow up, we don't fallback for source
|
||||
with self.assertRaises(TypeError) as ctx:
|
||||
manager._populate_and_plan('unit.tests.', [], [OtherType()],
|
||||
[])
|
||||
manager._populate_and_plan('unit.tests.', [], [OtherType()], [])
|
||||
self.assertEqual('something else', str(ctx.exception))
|
||||
|
||||
def test_plan_processors_fallback(self):
|
||||
@@ -402,23 +445,19 @@ class TestManager(TestCase):
|
||||
manager = Manager(get_config_filename('simple.yaml'))
|
||||
|
||||
class NoProcessors(SimpleProvider):
|
||||
|
||||
def plan(self, zone):
|
||||
pass
|
||||
|
||||
# This should be ok, we'll fall back to not passing it
|
||||
manager._populate_and_plan('unit.tests.', [], [],
|
||||
[NoProcessors()])
|
||||
manager._populate_and_plan('unit.tests.', [], [], [NoProcessors()])
|
||||
|
||||
class OtherType(SimpleProvider):
|
||||
|
||||
def plan(self, zone, processors):
|
||||
raise TypeError('something else')
|
||||
|
||||
# This will blow up, we don't fallback for source
|
||||
with self.assertRaises(TypeError) as ctx:
|
||||
manager._populate_and_plan('unit.tests.', [], [],
|
||||
[OtherType()])
|
||||
manager._populate_and_plan('unit.tests.', [], [], [OtherType()])
|
||||
self.assertEqual('something else', str(ctx.exception))
|
||||
|
||||
@patch('octodns.manager.Manager._get_named_class')
|
||||
@@ -429,8 +468,9 @@ class TestManager(TestCase):
|
||||
mock.return_value = (plan_output_class_mock, 'ignored', 'ignored')
|
||||
fh_mock = MagicMock()
|
||||
|
||||
Manager(get_config_filename('plan-output-filehandle.yaml')
|
||||
).sync(plan_output_fh=fh_mock)
|
||||
Manager(get_config_filename('plan-output-filehandle.yaml')).sync(
|
||||
plan_output_fh=fh_mock
|
||||
)
|
||||
|
||||
# Since we only care about the fh kwarg, and different _PlanOutputs are
|
||||
# are free to require arbitrary kwargs anyway, we concern ourselves
|
||||
@@ -449,18 +489,22 @@ class TestManager(TestCase):
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
# This zone specifies a non-existent processor
|
||||
manager.sync(['bad.unit.tests.'])
|
||||
self.assertTrue('Zone bad.unit.tests., unknown processor: '
|
||||
'doesnt-exist' in str(ctx.exception))
|
||||
self.assertTrue(
|
||||
'Zone bad.unit.tests., unknown processor: '
|
||||
'doesnt-exist' in str(ctx.exception)
|
||||
)
|
||||
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
Manager(get_config_filename('processors-missing-class.yaml'))
|
||||
self.assertTrue('Processor no-class is missing class' in
|
||||
str(ctx.exception))
|
||||
self.assertTrue(
|
||||
'Processor no-class is missing class' in str(ctx.exception)
|
||||
)
|
||||
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
Manager(get_config_filename('processors-wants-config.yaml'))
|
||||
self.assertTrue('Incorrect processor config for wants-config' in
|
||||
str(ctx.exception))
|
||||
self.assertTrue(
|
||||
'Incorrect processor config for wants-config' in str(ctx.exception)
|
||||
)
|
||||
|
||||
def test_processors(self):
|
||||
manager = Manager(get_config_filename('simple.yaml'))
|
||||
@@ -468,23 +512,21 @@ class TestManager(TestCase):
|
||||
targets = [PlannableProvider('prov')]
|
||||
|
||||
zone = Zone('unit.tests.', [])
|
||||
record = Record.new(zone, 'a', {
|
||||
'ttl': 30,
|
||||
'type': 'A',
|
||||
'value': '1.2.3.4',
|
||||
})
|
||||
record = Record.new(
|
||||
zone, 'a', {'ttl': 30, 'type': 'A', 'value': '1.2.3.4'}
|
||||
)
|
||||
|
||||
# muck with sources
|
||||
class MockProcessor(BaseProcessor):
|
||||
|
||||
def process_source_zone(self, zone, sources):
|
||||
zone = zone.copy()
|
||||
zone.add_record(record)
|
||||
return zone
|
||||
|
||||
mock = MockProcessor('mock')
|
||||
plans, zone = manager._populate_and_plan('unit.tests.', [mock], [],
|
||||
targets)
|
||||
plans, zone = manager._populate_and_plan(
|
||||
'unit.tests.', [mock], [], targets
|
||||
)
|
||||
# Our mock was called and added the record
|
||||
self.assertEqual(record, list(zone.records)[0])
|
||||
# We got a create for the thing added to the expected state (source)
|
||||
@@ -492,15 +534,15 @@ class TestManager(TestCase):
|
||||
|
||||
# muck with targets
|
||||
class MockProcessor(BaseProcessor):
|
||||
|
||||
def process_target_zone(self, zone, target):
|
||||
zone = zone.copy()
|
||||
zone.add_record(record)
|
||||
return zone
|
||||
|
||||
mock = MockProcessor('mock')
|
||||
plans, zone = manager._populate_and_plan('unit.tests.', [mock], [],
|
||||
targets)
|
||||
plans, zone = manager._populate_and_plan(
|
||||
'unit.tests.', [mock], [], targets
|
||||
)
|
||||
# No record added since it's target this time
|
||||
self.assertFalse(zone.records)
|
||||
# We got a delete for the thing added to the existing state (target)
|
||||
@@ -508,7 +550,6 @@ class TestManager(TestCase):
|
||||
|
||||
# muck with plans
|
||||
class MockProcessor(BaseProcessor):
|
||||
|
||||
def process_target_zone(self, zone, target):
|
||||
zone = zone.copy()
|
||||
zone.add_record(record)
|
||||
@@ -519,8 +560,9 @@ class TestManager(TestCase):
|
||||
plans.changes.pop(0)
|
||||
|
||||
mock = MockProcessor('mock')
|
||||
plans, zone = manager._populate_and_plan('unit.tests.', [mock], [],
|
||||
targets)
|
||||
plans, zone = manager._populate_and_plan(
|
||||
'unit.tests.', [mock], [], targets
|
||||
)
|
||||
# We planned a delete again, but this time removed it from the plan, so
|
||||
# no plans
|
||||
self.assertFalse(plans)
|
||||
@@ -534,23 +576,28 @@ class TestManager(TestCase):
|
||||
dummy_module = DummyModule()
|
||||
|
||||
# use importlib.metadata.version
|
||||
self.assertTrue(__VERSION__,
|
||||
manager._try_version('octodns',
|
||||
module=dummy_module,
|
||||
version='1.2.3'))
|
||||
self.assertTrue(
|
||||
__VERSION__,
|
||||
manager._try_version(
|
||||
'octodns', module=dummy_module, version='1.2.3'
|
||||
),
|
||||
)
|
||||
|
||||
# use module
|
||||
self.assertTrue(manager._try_version('doesnt-exist',
|
||||
module=dummy_module))
|
||||
self.assertTrue(
|
||||
manager._try_version('doesnt-exist', module=dummy_module)
|
||||
)
|
||||
|
||||
# fall back to version, preferred over module
|
||||
self.assertEqual('1.2.3', manager._try_version('doesnt-exist',
|
||||
module=dummy_module,
|
||||
version='1.2.3', ))
|
||||
self.assertEqual(
|
||||
'1.2.3',
|
||||
manager._try_version(
|
||||
'doesnt-exist', module=dummy_module, version='1.2.3'
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class TestMainThreadExecutor(TestCase):
|
||||
|
||||
def test_success(self):
|
||||
mte = MainThreadExecutor()
|
||||
|
||||
|
||||
+92
-78
@@ -2,15 +2,25 @@
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
from io import StringIO
|
||||
from logging import getLogger
|
||||
from unittest import TestCase
|
||||
|
||||
from octodns.provider.plan import Plan, PlanHtml, PlanLogger, PlanMarkdown, \
|
||||
RootNsChange, TooMuchChange
|
||||
from octodns.provider.plan import (
|
||||
Plan,
|
||||
PlanHtml,
|
||||
PlanLogger,
|
||||
PlanMarkdown,
|
||||
RootNsChange,
|
||||
TooMuchChange,
|
||||
)
|
||||
from octodns.record import Create, Delete, Record, Update
|
||||
from octodns.zone import Zone
|
||||
|
||||
@@ -19,32 +29,41 @@ from helpers import SimpleProvider
|
||||
|
||||
simple = SimpleProvider()
|
||||
zone = Zone('unit.tests.', [])
|
||||
existing = Record.new(zone, 'a', {
|
||||
'ttl': 300,
|
||||
'type': 'A',
|
||||
# This matches the zone data above, one to swap, one to leave
|
||||
'values': ['1.1.1.1', '2.2.2.2'],
|
||||
})
|
||||
new = Record.new(zone, 'a', {
|
||||
'geo': {
|
||||
'AF': ['5.5.5.5'],
|
||||
'NA-US': ['6.6.6.6']
|
||||
existing = Record.new(
|
||||
zone,
|
||||
'a',
|
||||
{
|
||||
'ttl': 300,
|
||||
'type': 'A',
|
||||
# This matches the zone data above, one to swap, one to leave
|
||||
'values': ['1.1.1.1', '2.2.2.2'],
|
||||
},
|
||||
'ttl': 300,
|
||||
'type': 'A',
|
||||
# This leaves one, swaps ones, and adds one
|
||||
'values': ['2.2.2.2', '3.3.3.3', '4.4.4.4'],
|
||||
}, simple)
|
||||
create = Create(Record.new(zone, 'b', {
|
||||
'ttl': 60,
|
||||
'type': 'CNAME',
|
||||
'value': 'foo.unit.tests.'
|
||||
}, simple))
|
||||
create2 = Create(Record.new(zone, 'c', {
|
||||
'ttl': 60,
|
||||
'type': 'CNAME',
|
||||
'value': 'foo.unit.tests.'
|
||||
}))
|
||||
)
|
||||
new = Record.new(
|
||||
zone,
|
||||
'a',
|
||||
{
|
||||
'geo': {'AF': ['5.5.5.5'], 'NA-US': ['6.6.6.6']},
|
||||
'ttl': 300,
|
||||
'type': 'A',
|
||||
# This leaves one, swaps ones, and adds one
|
||||
'values': ['2.2.2.2', '3.3.3.3', '4.4.4.4'],
|
||||
},
|
||||
simple,
|
||||
)
|
||||
create = Create(
|
||||
Record.new(
|
||||
zone,
|
||||
'b',
|
||||
{'ttl': 60, 'type': 'CNAME', 'value': 'foo.unit.tests.'},
|
||||
simple,
|
||||
)
|
||||
)
|
||||
create2 = Create(
|
||||
Record.new(
|
||||
zone, 'c', {'ttl': 60, 'type': 'CNAME', 'value': 'foo.unit.tests.'}
|
||||
)
|
||||
)
|
||||
update = Update(existing, new)
|
||||
delete = Delete(new)
|
||||
changes = [create, create2, delete, update]
|
||||
@@ -55,23 +74,18 @@ plans = [
|
||||
|
||||
|
||||
class TestPlanSortsChanges(TestCase):
|
||||
|
||||
def test_plan_sorts_changes_pass_to_it(self):
|
||||
# we aren't worried about the details of the sorting, that's tested in
|
||||
# test_octodns_record's TestChanges. We just want to make sure that the
|
||||
# changes are sorted at all.
|
||||
zone = Zone('unit.tests.', [])
|
||||
record_a_1 = Record.new(zone, '1', {
|
||||
'type': 'A',
|
||||
'ttl': 30,
|
||||
'value': '1.2.3.4',
|
||||
})
|
||||
record_a_1 = Record.new(
|
||||
zone, '1', {'type': 'A', 'ttl': 30, 'value': '1.2.3.4'}
|
||||
)
|
||||
create_a_1 = Create(record_a_1)
|
||||
record_a_2 = Record.new(zone, '2', {
|
||||
'type': 'A',
|
||||
'ttl': 30,
|
||||
'value': '1.2.3.4',
|
||||
})
|
||||
record_a_2 = Record.new(
|
||||
zone, '2', {'type': 'A', 'ttl': 30, 'value': '1.2.3.4'}
|
||||
)
|
||||
create_a_2 = Create(record_a_2)
|
||||
|
||||
# passed in reverse of expected order
|
||||
@@ -80,16 +94,13 @@ class TestPlanSortsChanges(TestCase):
|
||||
|
||||
|
||||
class TestPlanLogger(TestCase):
|
||||
|
||||
def test_invalid_level(self):
|
||||
with self.assertRaises(Exception) as ctx:
|
||||
PlanLogger('invalid', 'not-a-level')
|
||||
self.assertEqual('Unsupported level: not-a-level', str(ctx.exception))
|
||||
|
||||
def test_create(self):
|
||||
|
||||
class MockLogger(object):
|
||||
|
||||
def __init__(self):
|
||||
self.out = StringIO()
|
||||
|
||||
@@ -99,8 +110,10 @@ class TestPlanLogger(TestCase):
|
||||
log = MockLogger()
|
||||
PlanLogger('logger').run(log, plans)
|
||||
out = log.out.getvalue()
|
||||
self.assertTrue('Summary: Creates=2, Updates=1, '
|
||||
'Deletes=1, Existing Records=0' in out)
|
||||
self.assertTrue(
|
||||
'Summary: Creates=2, Updates=1, '
|
||||
'Deletes=1, Existing Records=0' in out
|
||||
)
|
||||
|
||||
|
||||
class TestPlanHtml(TestCase):
|
||||
@@ -115,8 +128,10 @@ class TestPlanHtml(TestCase):
|
||||
out = StringIO()
|
||||
PlanHtml('html').run(plans, fh=out)
|
||||
out = out.getvalue()
|
||||
self.assertTrue(' <td colspan=6>Summary: Creates=2, Updates=1, '
|
||||
'Deletes=1, Existing Records=0</td>' in out)
|
||||
self.assertTrue(
|
||||
' <td colspan=6>Summary: Creates=2, Updates=1, '
|
||||
'Deletes=1, Existing Records=0</td>' in out
|
||||
)
|
||||
|
||||
|
||||
class TestPlanMarkdown(TestCase):
|
||||
@@ -139,7 +154,6 @@ class TestPlanMarkdown(TestCase):
|
||||
|
||||
|
||||
class HelperPlan(Plan):
|
||||
|
||||
def __init__(self, *args, min_existing=0, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.MIN_EXISTING_RECORDS = min_existing
|
||||
@@ -147,26 +161,18 @@ class HelperPlan(Plan):
|
||||
|
||||
class TestPlanSafety(TestCase):
|
||||
existing = Zone('unit.tests.', [])
|
||||
record_1 = Record.new(existing, '1', data={
|
||||
'type': 'A',
|
||||
'ttl': 42,
|
||||
'value': '1.2.3.4',
|
||||
})
|
||||
record_2 = Record.new(existing, '2', data={
|
||||
'type': 'A',
|
||||
'ttl': 42,
|
||||
'value': '1.2.3.4',
|
||||
})
|
||||
record_3 = Record.new(existing, '3', data={
|
||||
'type': 'A',
|
||||
'ttl': 42,
|
||||
'value': '1.2.3.4',
|
||||
})
|
||||
record_4 = Record.new(existing, '4', data={
|
||||
'type': 'A',
|
||||
'ttl': 42,
|
||||
'value': '1.2.3.4',
|
||||
})
|
||||
record_1 = Record.new(
|
||||
existing, '1', data={'type': 'A', 'ttl': 42, 'value': '1.2.3.4'}
|
||||
)
|
||||
record_2 = Record.new(
|
||||
existing, '2', data={'type': 'A', 'ttl': 42, 'value': '1.2.3.4'}
|
||||
)
|
||||
record_3 = Record.new(
|
||||
existing, '3', data={'type': 'A', 'ttl': 42, 'value': '1.2.3.4'}
|
||||
)
|
||||
record_4 = Record.new(
|
||||
existing, '4', data={'type': 'A', 'ttl': 42, 'value': '1.2.3.4'}
|
||||
)
|
||||
|
||||
def test_too_many_updates(self):
|
||||
existing = self.existing.copy()
|
||||
@@ -267,11 +273,15 @@ class TestPlanSafety(TestCase):
|
||||
plan.raise_if_unsafe()
|
||||
|
||||
# Add a change to a non-root NS record, we're OK
|
||||
ns_record = Record.new(existing, 'sub', data={
|
||||
'type': 'NS',
|
||||
'ttl': 43,
|
||||
'values': ('ns1.unit.tests.', 'ns1.unit.tests.'),
|
||||
})
|
||||
ns_record = Record.new(
|
||||
existing,
|
||||
'sub',
|
||||
data={
|
||||
'type': 'NS',
|
||||
'ttl': 43,
|
||||
'values': ('ns1.unit.tests.', 'ns1.unit.tests.'),
|
||||
},
|
||||
)
|
||||
changes.append(Delete(ns_record))
|
||||
plan = HelperPlan(existing, None, changes, True)
|
||||
plan.raise_if_unsafe()
|
||||
@@ -279,11 +289,15 @@ class TestPlanSafety(TestCase):
|
||||
changes.pop(-1)
|
||||
|
||||
# Delete the root NS record and we get an unsafe
|
||||
root_ns_record = Record.new(existing, '', data={
|
||||
'type': 'NS',
|
||||
'ttl': 43,
|
||||
'values': ('ns3.unit.tests.', 'ns4.unit.tests.'),
|
||||
})
|
||||
root_ns_record = Record.new(
|
||||
existing,
|
||||
'',
|
||||
data={
|
||||
'type': 'NS',
|
||||
'ttl': 43,
|
||||
'values': ('ns3.unit.tests.', 'ns4.unit.tests.'),
|
||||
},
|
||||
)
|
||||
changes.append(Delete(root_ns_record))
|
||||
plan = HelperPlan(existing, None, changes, True)
|
||||
with self.assertRaises(RootNsChange) as ctx:
|
||||
|
||||
@@ -2,8 +2,12 @@
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
@@ -13,46 +17,43 @@ from octodns.zone import Zone
|
||||
|
||||
zone = Zone('unit.tests.', [])
|
||||
records = {
|
||||
'root-unowned': Record.new(zone, '_acme-challenge', {
|
||||
'ttl': 30,
|
||||
'type': 'TXT',
|
||||
'value': 'magic bit',
|
||||
}),
|
||||
'sub-unowned': Record.new(zone, '_acme-challenge.sub-unowned', {
|
||||
'ttl': 30,
|
||||
'type': 'TXT',
|
||||
'value': 'magic bit',
|
||||
}),
|
||||
'not-txt': Record.new(zone, '_acme-challenge.not-txt', {
|
||||
'ttl': 30,
|
||||
'type': 'AAAA',
|
||||
'value': '::1',
|
||||
}),
|
||||
'not-acme': Record.new(zone, 'not-acme', {
|
||||
'ttl': 30,
|
||||
'type': 'TXT',
|
||||
'value': 'Hello World!',
|
||||
}),
|
||||
'managed': Record.new(zone, '_acme-challenge.managed', {
|
||||
'ttl': 30,
|
||||
'type': 'TXT',
|
||||
'value': 'magic bit',
|
||||
}),
|
||||
'owned': Record.new(zone, '_acme-challenge.owned', {
|
||||
'ttl': 30,
|
||||
'type': 'TXT',
|
||||
'values': ['*octoDNS*', 'magic bit'],
|
||||
}),
|
||||
'going-away': Record.new(zone, '_acme-challenge.going-away', {
|
||||
'ttl': 30,
|
||||
'type': 'TXT',
|
||||
'values': ['*octoDNS*', 'magic bit'],
|
||||
}),
|
||||
'root-unowned': Record.new(
|
||||
zone,
|
||||
'_acme-challenge',
|
||||
{'ttl': 30, 'type': 'TXT', 'value': 'magic bit'},
|
||||
),
|
||||
'sub-unowned': Record.new(
|
||||
zone,
|
||||
'_acme-challenge.sub-unowned',
|
||||
{'ttl': 30, 'type': 'TXT', 'value': 'magic bit'},
|
||||
),
|
||||
'not-txt': Record.new(
|
||||
zone,
|
||||
'_acme-challenge.not-txt',
|
||||
{'ttl': 30, 'type': 'AAAA', 'value': '::1'},
|
||||
),
|
||||
'not-acme': Record.new(
|
||||
zone, 'not-acme', {'ttl': 30, 'type': 'TXT', 'value': 'Hello World!'}
|
||||
),
|
||||
'managed': Record.new(
|
||||
zone,
|
||||
'_acme-challenge.managed',
|
||||
{'ttl': 30, 'type': 'TXT', 'value': 'magic bit'},
|
||||
),
|
||||
'owned': Record.new(
|
||||
zone,
|
||||
'_acme-challenge.owned',
|
||||
{'ttl': 30, 'type': 'TXT', 'values': ['*octoDNS*', 'magic bit']},
|
||||
),
|
||||
'going-away': Record.new(
|
||||
zone,
|
||||
'_acme-challenge.going-away',
|
||||
{'ttl': 30, 'type': 'TXT', 'values': ['*octoDNS*', 'magic bit']},
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
class TestAcmeMangingProcessor(TestCase):
|
||||
|
||||
def test_process_zones(self):
|
||||
acme = AcmeMangingProcessor('acme')
|
||||
|
||||
@@ -64,11 +65,10 @@ class TestAcmeMangingProcessor(TestCase):
|
||||
source.add_record(records['managed'])
|
||||
|
||||
got = acme.process_source_zone(source)
|
||||
self.assertEqual([
|
||||
'_acme-challenge.managed',
|
||||
'_acme-challenge.not-txt',
|
||||
'not-acme',
|
||||
], sorted([r.name for r in got.records]))
|
||||
self.assertEqual(
|
||||
['_acme-challenge.managed', '_acme-challenge.not-txt', 'not-acme'],
|
||||
sorted([r.name for r in got.records]),
|
||||
)
|
||||
managed = None
|
||||
for record in got.records:
|
||||
if record.name.endswith('managed'):
|
||||
@@ -93,10 +93,13 @@ class TestAcmeMangingProcessor(TestCase):
|
||||
existing.add_record(records['going-away'])
|
||||
|
||||
got = acme.process_target_zone(existing)
|
||||
self.assertEqual([
|
||||
'_acme-challenge.going-away',
|
||||
'_acme-challenge.managed',
|
||||
'_acme-challenge.not-txt',
|
||||
'_acme-challenge.owned',
|
||||
'not-acme'
|
||||
], sorted([r.name for r in got.records]))
|
||||
self.assertEqual(
|
||||
[
|
||||
'_acme-challenge.going-away',
|
||||
'_acme-challenge.managed',
|
||||
'_acme-challenge.not-txt',
|
||||
'_acme-challenge.owned',
|
||||
'not-acme',
|
||||
],
|
||||
sorted([r.name for r in got.records]),
|
||||
)
|
||||
|
||||
@@ -2,15 +2,19 @@
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
|
||||
class TestAwsAcmMangingProcessor(TestCase):
|
||||
|
||||
def test_missing(self):
|
||||
with self.assertRaises(ModuleNotFoundError):
|
||||
from octodns.processor.awsacm import AwsAcmMangingProcessor
|
||||
|
||||
AwsAcmMangingProcessor
|
||||
|
||||
@@ -2,8 +2,12 @@
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
@@ -13,37 +17,20 @@ from octodns.zone import Zone
|
||||
|
||||
zone = Zone('unit.tests.', [])
|
||||
for record in [
|
||||
Record.new(zone, 'a', {
|
||||
'ttl': 30,
|
||||
'type': 'A',
|
||||
'value': '1.2.3.4',
|
||||
}),
|
||||
Record.new(zone, 'aaaa', {
|
||||
'ttl': 30,
|
||||
'type': 'AAAA',
|
||||
'value': '::1',
|
||||
}),
|
||||
Record.new(zone, 'txt', {
|
||||
'ttl': 30,
|
||||
'type': 'TXT',
|
||||
'value': 'Hello World!',
|
||||
}),
|
||||
Record.new(zone, 'a2', {
|
||||
'ttl': 30,
|
||||
'type': 'A',
|
||||
'value': '2.3.4.5',
|
||||
}),
|
||||
Record.new(zone, 'txt2', {
|
||||
'ttl': 30,
|
||||
'type': 'TXT',
|
||||
'value': 'That will do',
|
||||
}),
|
||||
Record.new(zone, 'a', {'ttl': 30, 'type': 'A', 'value': '1.2.3.4'}),
|
||||
Record.new(zone, 'aaaa', {'ttl': 30, 'type': 'AAAA', 'value': '::1'}),
|
||||
Record.new(
|
||||
zone, 'txt', {'ttl': 30, 'type': 'TXT', 'value': 'Hello World!'}
|
||||
),
|
||||
Record.new(zone, 'a2', {'ttl': 30, 'type': 'A', 'value': '2.3.4.5'}),
|
||||
Record.new(
|
||||
zone, 'txt2', {'ttl': 30, 'type': 'TXT', 'value': 'That will do'}
|
||||
),
|
||||
]:
|
||||
zone.add_record(record)
|
||||
|
||||
|
||||
class TestTypeAllowListFilter(TestCase):
|
||||
|
||||
def test_basics(self):
|
||||
filter_a = TypeAllowlistFilter('only-a', set(('A')))
|
||||
|
||||
@@ -56,35 +43,36 @@ class TestTypeAllowListFilter(TestCase):
|
||||
|
||||
filter_txt = TypeAllowlistFilter('only-txt', ['TXT'])
|
||||
got = filter_txt.process_target_zone(zone.copy())
|
||||
self.assertEqual(['txt', 'txt2'],
|
||||
sorted([r.name for r in got.records]))
|
||||
self.assertEqual(['txt', 'txt2'], sorted([r.name for r in got.records]))
|
||||
|
||||
filter_a_aaaa = TypeAllowlistFilter('only-aaaa', set(('A', 'AAAA')))
|
||||
got = filter_a_aaaa.process_target_zone(zone.copy())
|
||||
self.assertEqual(['a', 'a2', 'aaaa'],
|
||||
sorted([r.name for r in got.records]))
|
||||
self.assertEqual(
|
||||
['a', 'a2', 'aaaa'], sorted([r.name for r in got.records])
|
||||
)
|
||||
|
||||
|
||||
class TestTypeRejectListFilter(TestCase):
|
||||
|
||||
def test_basics(self):
|
||||
filter_a = TypeRejectlistFilter('not-a', set(('A')))
|
||||
|
||||
got = filter_a.process_source_zone(zone.copy())
|
||||
self.assertEqual(['aaaa', 'txt', 'txt2'],
|
||||
sorted([r.name for r in got.records]))
|
||||
self.assertEqual(
|
||||
['aaaa', 'txt', 'txt2'], sorted([r.name for r in got.records])
|
||||
)
|
||||
|
||||
filter_aaaa = TypeRejectlistFilter('not-aaaa', ('AAAA',))
|
||||
got = filter_aaaa.process_source_zone(zone.copy())
|
||||
self.assertEqual(['a', 'a2', 'txt', 'txt2'],
|
||||
sorted([r.name for r in got.records]))
|
||||
self.assertEqual(
|
||||
['a', 'a2', 'txt', 'txt2'], sorted([r.name for r in got.records])
|
||||
)
|
||||
|
||||
filter_txt = TypeRejectlistFilter('not-txt', ['TXT'])
|
||||
got = filter_txt.process_target_zone(zone.copy())
|
||||
self.assertEqual(['a', 'a2', 'aaaa'],
|
||||
sorted([r.name for r in got.records]))
|
||||
self.assertEqual(
|
||||
['a', 'a2', 'aaaa'], sorted([r.name for r in got.records])
|
||||
)
|
||||
|
||||
filter_a_aaaa = TypeRejectlistFilter('not-a-aaaa', set(('A', 'AAAA')))
|
||||
got = filter_a_aaaa.process_target_zone(zone.copy())
|
||||
self.assertEqual(['txt', 'txt2'],
|
||||
sorted([r.name for r in got.records]))
|
||||
self.assertEqual(['txt', 'txt2'], sorted([r.name for r in got.records]))
|
||||
|
||||
@@ -2,8 +2,12 @@
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
@@ -17,57 +21,40 @@ from helpers import PlannableProvider
|
||||
zone = Zone('unit.tests.', [])
|
||||
records = {}
|
||||
for record in [
|
||||
Record.new(zone, '', {
|
||||
'ttl': 30,
|
||||
'type': 'A',
|
||||
'values': [
|
||||
'1.2.3.4',
|
||||
'5.6.7.8',
|
||||
],
|
||||
}),
|
||||
Record.new(zone, 'the-a', {
|
||||
'ttl': 30,
|
||||
'type': 'A',
|
||||
'value': '1.2.3.4',
|
||||
}),
|
||||
Record.new(zone, 'the-aaaa', {
|
||||
'ttl': 30,
|
||||
'type': 'AAAA',
|
||||
'value': '::1',
|
||||
}),
|
||||
Record.new(zone, 'the-txt', {
|
||||
'ttl': 30,
|
||||
'type': 'TXT',
|
||||
'value': 'Hello World!',
|
||||
}),
|
||||
Record.new(zone, '*', {
|
||||
'ttl': 30,
|
||||
'type': 'A',
|
||||
'value': '4.3.2.1',
|
||||
}),
|
||||
Record.new(
|
||||
zone, '', {'ttl': 30, 'type': 'A', 'values': ['1.2.3.4', '5.6.7.8']}
|
||||
),
|
||||
Record.new(zone, 'the-a', {'ttl': 30, 'type': 'A', 'value': '1.2.3.4'}),
|
||||
Record.new(zone, 'the-aaaa', {'ttl': 30, 'type': 'AAAA', 'value': '::1'}),
|
||||
Record.new(
|
||||
zone, 'the-txt', {'ttl': 30, 'type': 'TXT', 'value': 'Hello World!'}
|
||||
),
|
||||
Record.new(zone, '*', {'ttl': 30, 'type': 'A', 'value': '4.3.2.1'}),
|
||||
]:
|
||||
records[record.name] = record
|
||||
zone.add_record(record)
|
||||
|
||||
|
||||
class TestOwnershipProcessor(TestCase):
|
||||
|
||||
def test_process_source_zone(self):
|
||||
ownership = OwnershipProcessor('ownership')
|
||||
|
||||
got = ownership.process_source_zone(zone.copy())
|
||||
self.assertEqual([
|
||||
'',
|
||||
'*',
|
||||
'_owner.a',
|
||||
'_owner.a._wildcard',
|
||||
'_owner.a.the-a',
|
||||
'_owner.aaaa.the-aaaa',
|
||||
'_owner.txt.the-txt',
|
||||
'the-a',
|
||||
'the-aaaa',
|
||||
'the-txt',
|
||||
], sorted([r.name for r in got.records]))
|
||||
self.assertEqual(
|
||||
[
|
||||
'',
|
||||
'*',
|
||||
'_owner.a',
|
||||
'_owner.a._wildcard',
|
||||
'_owner.a.the-a',
|
||||
'_owner.aaaa.the-aaaa',
|
||||
'_owner.txt.the-txt',
|
||||
'the-a',
|
||||
'the-aaaa',
|
||||
'the-txt',
|
||||
],
|
||||
sorted([r.name for r in got.records]),
|
||||
)
|
||||
|
||||
found = False
|
||||
for record in got.records:
|
||||
@@ -101,11 +88,9 @@ class TestOwnershipProcessor(TestCase):
|
||||
|
||||
# Something extra exists and doesn't have ownership TXT, leave it
|
||||
# alone, we don't own it.
|
||||
extra_a = Record.new(zone, 'extra-a', {
|
||||
'ttl': 30,
|
||||
'type': 'A',
|
||||
'value': '4.4.4.4',
|
||||
})
|
||||
extra_a = Record.new(
|
||||
zone, 'extra-a', {'ttl': 30, 'type': 'A', 'value': '4.4.4.4'}
|
||||
)
|
||||
plan.existing.add_record(extra_a)
|
||||
# If we'd done a "real" plan we'd have a delete for the extra thing.
|
||||
plan.changes.append(Delete(extra_a))
|
||||
@@ -130,11 +115,9 @@ class TestOwnershipProcessor(TestCase):
|
||||
the_a = records['the-a']
|
||||
plan.existing.add_record(the_a)
|
||||
name = f'{ownership.txt_name}.a.the-a'
|
||||
the_a_ownership = Record.new(zone, name, {
|
||||
'ttl': 30,
|
||||
'type': 'TXT',
|
||||
'value': ownership.txt_value,
|
||||
})
|
||||
the_a_ownership = Record.new(
|
||||
zone, name, {'ttl': 30, 'type': 'TXT', 'value': ownership.txt_value}
|
||||
)
|
||||
plan.existing.add_record(the_a_ownership)
|
||||
plan.changes.append(Delete(the_a))
|
||||
plan.changes.append(Delete(the_a_ownership))
|
||||
|
||||
@@ -2,15 +2,19 @@
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
|
||||
class TestAzureShim(TestCase):
|
||||
|
||||
def test_missing(self):
|
||||
with self.assertRaises(ModuleNotFoundError):
|
||||
from octodns.provider.azuredns import AzureProvider
|
||||
|
||||
AzureProvider
|
||||
|
||||
+273
-262
@@ -2,8 +2,12 @@
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
from logging import getLogger
|
||||
from unittest import TestCase
|
||||
@@ -27,8 +31,12 @@ class HelperProvider(BaseProvider):
|
||||
id = 'test'
|
||||
strict_supports = False
|
||||
|
||||
def __init__(self, extra_changes=[], apply_disabled=False,
|
||||
include_change_callback=None):
|
||||
def __init__(
|
||||
self,
|
||||
extra_changes=[],
|
||||
apply_disabled=False,
|
||||
include_change_callback=None,
|
||||
):
|
||||
self.__extra_changes = extra_changes
|
||||
self.apply_disabled = apply_disabled
|
||||
self.include_change_callback = include_change_callback
|
||||
@@ -39,8 +47,9 @@ class HelperProvider(BaseProvider):
|
||||
return True
|
||||
|
||||
def _include_change(self, change):
|
||||
return not self.include_change_callback or \
|
||||
self.include_change_callback(change)
|
||||
return not self.include_change_callback or self.include_change_callback(
|
||||
change
|
||||
)
|
||||
|
||||
def _extra_changes(self, **kwargs):
|
||||
return self.__extra_changes
|
||||
@@ -50,7 +59,6 @@ class HelperProvider(BaseProvider):
|
||||
|
||||
|
||||
class TrickyProcessor(BaseProcessor):
|
||||
|
||||
def __init__(self, name, add_during_process_target_zone):
|
||||
super(TrickyProcessor, self).__init__(name)
|
||||
self.add_during_process_target_zone = add_during_process_target_zone
|
||||
@@ -73,20 +81,22 @@ class TrickyProcessor(BaseProcessor):
|
||||
|
||||
|
||||
class TestBaseProvider(TestCase):
|
||||
|
||||
def test_base_provider(self):
|
||||
with self.assertRaises(NotImplementedError) as ctx:
|
||||
BaseProvider('base')
|
||||
self.assertEqual('Abstract base class, log property missing',
|
||||
str(ctx.exception))
|
||||
self.assertEqual(
|
||||
'Abstract base class, log property missing', str(ctx.exception)
|
||||
)
|
||||
|
||||
class HasLog(BaseProvider):
|
||||
log = getLogger('HasLog')
|
||||
|
||||
with self.assertRaises(NotImplementedError) as ctx:
|
||||
HasLog('haslog')
|
||||
self.assertEqual('Abstract base class, SUPPORTS_GEO property missing',
|
||||
str(ctx.exception))
|
||||
self.assertEqual(
|
||||
'Abstract base class, SUPPORTS_GEO property missing',
|
||||
str(ctx.exception),
|
||||
)
|
||||
|
||||
class HasSupportsGeo(HasLog):
|
||||
SUPPORTS_GEO = False
|
||||
@@ -94,15 +104,18 @@ class TestBaseProvider(TestCase):
|
||||
zone = Zone('unit.tests.', ['sub'])
|
||||
with self.assertRaises(NotImplementedError) as ctx:
|
||||
HasSupportsGeo('hassupportsgeo').populate(zone)
|
||||
self.assertEqual('Abstract base class, SUPPORTS property missing',
|
||||
str(ctx.exception))
|
||||
self.assertEqual(
|
||||
'Abstract base class, SUPPORTS property missing', str(ctx.exception)
|
||||
)
|
||||
|
||||
class HasSupports(HasSupportsGeo):
|
||||
SUPPORTS = set(('A',))
|
||||
|
||||
with self.assertRaises(NotImplementedError) as ctx:
|
||||
HasSupports('hassupports').populate(zone)
|
||||
self.assertEqual('Abstract base class, populate method missing',
|
||||
str(ctx.exception))
|
||||
self.assertEqual(
|
||||
'Abstract base class, populate method missing', str(ctx.exception)
|
||||
)
|
||||
|
||||
# SUPPORTS_DYNAMIC has a default/fallback
|
||||
self.assertFalse(HasSupports('hassupports').SUPPORTS_DYNAMIC)
|
||||
@@ -111,44 +124,51 @@ class TestBaseProvider(TestCase):
|
||||
class HasSupportsDyanmic(HasSupports):
|
||||
SUPPORTS_DYNAMIC = True
|
||||
|
||||
self.assertTrue(HasSupportsDyanmic('hassupportsdynamic')
|
||||
.SUPPORTS_DYNAMIC)
|
||||
self.assertTrue(
|
||||
HasSupportsDyanmic('hassupportsdynamic').SUPPORTS_DYNAMIC
|
||||
)
|
||||
|
||||
class HasPopulate(HasSupports):
|
||||
|
||||
def populate(self, zone, target=False, lenient=False):
|
||||
zone.add_record(Record.new(zone, '', {
|
||||
'ttl': 60,
|
||||
'type': 'A',
|
||||
'value': '2.3.4.5'
|
||||
}), lenient=lenient)
|
||||
zone.add_record(Record.new(zone, 'going', {
|
||||
'ttl': 60,
|
||||
'type': 'A',
|
||||
'value': '3.4.5.6'
|
||||
}), lenient=lenient)
|
||||
zone.add_record(Record.new(zone, 'foo.sub', {
|
||||
'ttl': 61,
|
||||
'type': 'A',
|
||||
'value': '4.5.6.7'
|
||||
}), lenient=lenient)
|
||||
zone.add_record(
|
||||
Record.new(
|
||||
zone, '', {'ttl': 60, 'type': 'A', 'value': '2.3.4.5'}
|
||||
),
|
||||
lenient=lenient,
|
||||
)
|
||||
zone.add_record(
|
||||
Record.new(
|
||||
zone,
|
||||
'going',
|
||||
{'ttl': 60, 'type': 'A', 'value': '3.4.5.6'},
|
||||
),
|
||||
lenient=lenient,
|
||||
)
|
||||
zone.add_record(
|
||||
Record.new(
|
||||
zone,
|
||||
'foo.sub',
|
||||
{'ttl': 61, 'type': 'A', 'value': '4.5.6.7'},
|
||||
),
|
||||
lenient=lenient,
|
||||
)
|
||||
|
||||
zone.add_record(Record.new(zone, '', {
|
||||
'ttl': 60,
|
||||
'type': 'A',
|
||||
'value': '1.2.3.4'
|
||||
}))
|
||||
zone.add_record(
|
||||
Record.new(zone, '', {'ttl': 60, 'type': 'A', 'value': '1.2.3.4'})
|
||||
)
|
||||
|
||||
self.assertTrue(HasSupports('hassupportsgeo')
|
||||
.supports(list(zone.records)[0]))
|
||||
self.assertTrue(
|
||||
HasSupports('hassupportsgeo').supports(list(zone.records)[0])
|
||||
)
|
||||
|
||||
plan = HasPopulate('haspopulate').plan(zone)
|
||||
self.assertEqual(3, len(plan.changes))
|
||||
|
||||
with self.assertRaises(NotImplementedError) as ctx:
|
||||
HasPopulate('haspopulate').apply(plan)
|
||||
self.assertEqual('Abstract base class, _apply method missing',
|
||||
str(ctx.exception))
|
||||
self.assertEqual(
|
||||
'Abstract base class, _apply method missing', str(ctx.exception)
|
||||
)
|
||||
|
||||
def test_plan(self):
|
||||
ignored = Zone('unit.tests.', [])
|
||||
@@ -157,11 +177,9 @@ class TestBaseProvider(TestCase):
|
||||
provider = HelperProvider([])
|
||||
self.assertEqual(None, provider.plan(ignored))
|
||||
|
||||
record = Record.new(ignored, 'a', {
|
||||
'ttl': 30,
|
||||
'type': 'A',
|
||||
'value': '1.2.3.4',
|
||||
})
|
||||
record = Record.new(
|
||||
ignored, 'a', {'ttl': 30, 'type': 'A', 'value': '1.2.3.4'}
|
||||
)
|
||||
provider = HelperProvider([Create(record)])
|
||||
plan = provider.plan(ignored)
|
||||
self.assertTrue(plan)
|
||||
@@ -171,7 +189,6 @@ class TestBaseProvider(TestCase):
|
||||
ignored = Zone('unit.tests.', [])
|
||||
|
||||
class OldApiProvider(HelperProvider):
|
||||
|
||||
def _process_desired_zone(self, desired):
|
||||
return desired
|
||||
|
||||
@@ -180,7 +197,6 @@ class TestBaseProvider(TestCase):
|
||||
self.assertEqual(None, provider.plan(ignored))
|
||||
|
||||
class OtherTypeErrorProvider(HelperProvider):
|
||||
|
||||
def _process_desired_zone(self, desired, exists=False):
|
||||
raise TypeError('foo')
|
||||
|
||||
@@ -193,18 +209,20 @@ class TestBaseProvider(TestCase):
|
||||
zone = Zone('unit.tests.', [])
|
||||
|
||||
# supported
|
||||
supported = Record.new(zone, 'a', {
|
||||
'ttl': 30,
|
||||
'type': 'A',
|
||||
'value': '1.2.3.4',
|
||||
})
|
||||
supported = Record.new(
|
||||
zone, 'a', {'ttl': 30, 'type': 'A', 'value': '1.2.3.4'}
|
||||
)
|
||||
zone.add_record(supported)
|
||||
# not supported
|
||||
not_supported = Record.new(zone, 'aaaa', {
|
||||
'ttl': 30,
|
||||
'type': 'AAAA',
|
||||
'value': '2601:644:500:e210:62f8:1dff:feb8:947a',
|
||||
})
|
||||
not_supported = Record.new(
|
||||
zone,
|
||||
'aaaa',
|
||||
{
|
||||
'ttl': 30,
|
||||
'type': 'AAAA',
|
||||
'value': '2601:644:500:e210:62f8:1dff:feb8:947a',
|
||||
},
|
||||
)
|
||||
zone.add_record(not_supported)
|
||||
provider = HelperProvider()
|
||||
plan = provider.plan(zone)
|
||||
@@ -215,11 +233,9 @@ class TestBaseProvider(TestCase):
|
||||
def test_plan_with_processors(self):
|
||||
zone = Zone('unit.tests.', [])
|
||||
|
||||
record = Record.new(zone, 'a', {
|
||||
'ttl': 30,
|
||||
'type': 'A',
|
||||
'value': '1.2.3.4',
|
||||
})
|
||||
record = Record.new(
|
||||
zone, 'a', {'ttl': 30, 'type': 'A', 'value': '1.2.3.4'}
|
||||
)
|
||||
provider = HelperProvider()
|
||||
# Processor that adds a record to the zone, which planning will then
|
||||
# delete since it won't know anything about it
|
||||
@@ -233,11 +249,9 @@ class TestBaseProvider(TestCase):
|
||||
self.assertEqual(zone.name, tricky.existing.name)
|
||||
|
||||
# Chain of processors happen one after the other
|
||||
other = Record.new(zone, 'b', {
|
||||
'ttl': 30,
|
||||
'type': 'A',
|
||||
'value': '5.6.7.8',
|
||||
})
|
||||
other = Record.new(
|
||||
zone, 'b', {'ttl': 30, 'type': 'A', 'value': '5.6.7.8'}
|
||||
)
|
||||
# Another processor will add its record, thus 2 deletes
|
||||
another = TrickyProcessor('tricky', [other])
|
||||
plan = provider.plan(zone, processors=[tricky, another])
|
||||
@@ -253,11 +267,9 @@ class TestBaseProvider(TestCase):
|
||||
|
||||
def test_plan_with_root_ns(self):
|
||||
zone = Zone('unit.tests.', [])
|
||||
record = Record.new(zone, '', {
|
||||
'ttl': 30,
|
||||
'type': 'NS',
|
||||
'value': '1.2.3.4.',
|
||||
})
|
||||
record = Record.new(
|
||||
zone, '', {'ttl': 30, 'type': 'NS', 'value': '1.2.3.4.'}
|
||||
)
|
||||
zone.add_record(record)
|
||||
|
||||
# No root NS support, no change, thus no plan
|
||||
@@ -273,11 +285,9 @@ class TestBaseProvider(TestCase):
|
||||
def test_apply(self):
|
||||
ignored = Zone('unit.tests.', [])
|
||||
|
||||
record = Record.new(ignored, 'a', {
|
||||
'ttl': 30,
|
||||
'type': 'A',
|
||||
'value': '1.2.3.4',
|
||||
})
|
||||
record = Record.new(
|
||||
ignored, 'a', {'ttl': 30, 'type': 'A', 'value': '1.2.3.4'}
|
||||
)
|
||||
provider = HelperProvider([Create(record)], apply_disabled=True)
|
||||
plan = provider.plan(ignored)
|
||||
provider.apply(plan)
|
||||
@@ -288,11 +298,9 @@ class TestBaseProvider(TestCase):
|
||||
def test_include_change(self):
|
||||
zone = Zone('unit.tests.', [])
|
||||
|
||||
record = Record.new(zone, 'a', {
|
||||
'ttl': 30,
|
||||
'type': 'A',
|
||||
'value': '1.2.3.4',
|
||||
})
|
||||
record = Record.new(
|
||||
zone, 'a', {'ttl': 30, 'type': 'A', 'value': '1.2.3.4'}
|
||||
)
|
||||
zone.add_record(record)
|
||||
provider = HelperProvider([], include_change_callback=lambda c: False)
|
||||
plan = provider.plan(zone)
|
||||
@@ -300,7 +308,6 @@ class TestBaseProvider(TestCase):
|
||||
self.assertFalse(plan)
|
||||
|
||||
def test_plan_order_of_operations(self):
|
||||
|
||||
class MockProvider(BaseProvider):
|
||||
log = getLogger('mock-provider')
|
||||
SUPPORTS = set(('A',))
|
||||
@@ -327,8 +334,10 @@ class TestBaseProvider(TestCase):
|
||||
self.assertFalse(provider.plan(zone))
|
||||
# ensure the calls were made in the expected order, populate comes
|
||||
# first, then desired, then existing
|
||||
self.assertEqual(['populate', '_process_desired_zone',
|
||||
'_process_existing_zone'], provider.calls)
|
||||
self.assertEqual(
|
||||
['populate', '_process_desired_zone', '_process_existing_zone'],
|
||||
provider.calls,
|
||||
)
|
||||
|
||||
def test_process_desired_zone(self):
|
||||
provider = HelperProvider('test')
|
||||
@@ -336,11 +345,11 @@ class TestBaseProvider(TestCase):
|
||||
# SUPPORTS_MULTIVALUE_PTR
|
||||
provider.SUPPORTS_MULTIVALUE_PTR = False
|
||||
zone1 = Zone('unit.tests.', [])
|
||||
record1 = Record.new(zone1, 'ptr', {
|
||||
'type': 'PTR',
|
||||
'ttl': 3600,
|
||||
'values': ['foo.com.', 'bar.com.'],
|
||||
})
|
||||
record1 = Record.new(
|
||||
zone1,
|
||||
'ptr',
|
||||
{'type': 'PTR', 'ttl': 3600, 'values': ['foo.com.', 'bar.com.']},
|
||||
)
|
||||
zone1.add_record(record1)
|
||||
|
||||
zone2 = provider._process_desired_zone(zone1.copy())
|
||||
@@ -355,23 +364,19 @@ class TestBaseProvider(TestCase):
|
||||
# SUPPORTS_DYNAMIC
|
||||
provider.SUPPORTS_DYNAMIC = False
|
||||
zone1 = Zone('unit.tests.', [])
|
||||
record1 = Record.new(zone1, 'a', {
|
||||
'dynamic': {
|
||||
'pools': {
|
||||
'one': {
|
||||
'values': [{
|
||||
'value': '1.1.1.1',
|
||||
}],
|
||||
},
|
||||
record1 = Record.new(
|
||||
zone1,
|
||||
'a',
|
||||
{
|
||||
'dynamic': {
|
||||
'pools': {'one': {'values': [{'value': '1.1.1.1'}]}},
|
||||
'rules': [{'pool': 'one'}],
|
||||
},
|
||||
'rules': [{
|
||||
'pool': 'one',
|
||||
}],
|
||||
'type': 'A',
|
||||
'ttl': 3600,
|
||||
'values': ['2.2.2.2'],
|
||||
},
|
||||
'type': 'A',
|
||||
'ttl': 3600,
|
||||
'values': ['2.2.2.2'],
|
||||
})
|
||||
)
|
||||
self.assertTrue(record1.dynamic)
|
||||
zone1.add_record(record1)
|
||||
|
||||
@@ -393,18 +398,17 @@ class TestBaseProvider(TestCase):
|
||||
zone2 = provider._process_desired_zone(zone1.copy())
|
||||
record2 = list(zone2.records)[0]
|
||||
self.assertEqual(
|
||||
record2.dynamic.pools['one'].data['values'][0]['status'],
|
||||
'obey'
|
||||
record2.dynamic.pools['one'].data['values'][0]['status'], 'obey'
|
||||
)
|
||||
|
||||
# SUPPORTS_ROOT_NS
|
||||
provider.SUPPORTS_ROOT_NS = False
|
||||
zone1 = Zone('unit.tests.', [])
|
||||
record1 = Record.new(zone1, '', {
|
||||
'type': 'NS',
|
||||
'ttl': 3600,
|
||||
'values': ['foo.com.', 'bar.com.'],
|
||||
})
|
||||
record1 = Record.new(
|
||||
zone1,
|
||||
'',
|
||||
{'type': 'NS', 'ttl': 3600, 'values': ['foo.com.', 'bar.com.']},
|
||||
)
|
||||
zone1.add_record(record1)
|
||||
|
||||
zone2 = provider._process_desired_zone(zone1.copy())
|
||||
@@ -421,11 +425,11 @@ class TestBaseProvider(TestCase):
|
||||
# SUPPORTS_ROOT_NS
|
||||
provider.SUPPORTS_ROOT_NS = False
|
||||
zone1 = Zone('unit.tests.', [])
|
||||
record1 = Record.new(zone1, '', {
|
||||
'type': 'NS',
|
||||
'ttl': 3600,
|
||||
'values': ['foo.com.', 'bar.com.'],
|
||||
})
|
||||
record1 = Record.new(
|
||||
zone1,
|
||||
'',
|
||||
{'type': 'NS', 'ttl': 3600, 'values': ['foo.com.', 'bar.com.']},
|
||||
)
|
||||
zone1.add_record(record1)
|
||||
|
||||
zone2 = provider._process_existing_zone(zone1.copy(), zone1)
|
||||
@@ -444,42 +448,38 @@ class TestBaseProvider(TestCase):
|
||||
# Creates are safe when existing records is under MIN_EXISTING_RECORDS
|
||||
zone = Zone('unit.tests.', [])
|
||||
|
||||
record = Record.new(zone, 'a', {
|
||||
'ttl': 30,
|
||||
'type': 'A',
|
||||
'value': '1.2.3.4',
|
||||
})
|
||||
Plan(zone, zone, [Create(record) for i in range(10)], True) \
|
||||
.raise_if_unsafe()
|
||||
record = Record.new(
|
||||
zone, 'a', {'ttl': 30, 'type': 'A', 'value': '1.2.3.4'}
|
||||
)
|
||||
Plan(
|
||||
zone, zone, [Create(record) for i in range(10)], True
|
||||
).raise_if_unsafe()
|
||||
|
||||
def test_safe_min_existing_creates(self):
|
||||
# Creates are safe when existing records is over MIN_EXISTING_RECORDS
|
||||
zone = Zone('unit.tests.', [])
|
||||
|
||||
record = Record.new(zone, 'a', {
|
||||
'ttl': 30,
|
||||
'type': 'A',
|
||||
'value': '1.2.3.4',
|
||||
})
|
||||
record = Record.new(
|
||||
zone, 'a', {'ttl': 30, 'type': 'A', 'value': '1.2.3.4'}
|
||||
)
|
||||
|
||||
for i in range(int(Plan.MIN_EXISTING_RECORDS)):
|
||||
zone.add_record(Record.new(zone, str(i), {
|
||||
'ttl': 60,
|
||||
'type': 'A',
|
||||
'value': '2.3.4.5'
|
||||
}))
|
||||
zone.add_record(
|
||||
Record.new(
|
||||
zone, str(i), {'ttl': 60, 'type': 'A', 'value': '2.3.4.5'}
|
||||
)
|
||||
)
|
||||
|
||||
Plan(zone, zone, [Create(record) for i in range(10)], True) \
|
||||
.raise_if_unsafe()
|
||||
Plan(
|
||||
zone, zone, [Create(record) for i in range(10)], True
|
||||
).raise_if_unsafe()
|
||||
|
||||
def test_safe_no_existing(self):
|
||||
# existing records fewer than MIN_EXISTING_RECORDS is safe
|
||||
zone = Zone('unit.tests.', [])
|
||||
record = Record.new(zone, 'a', {
|
||||
'ttl': 30,
|
||||
'type': 'A',
|
||||
'value': '1.2.3.4',
|
||||
})
|
||||
record = Record.new(
|
||||
zone, 'a', {'ttl': 30, 'type': 'A', 'value': '1.2.3.4'}
|
||||
)
|
||||
|
||||
updates = [Update(record, record), Update(record, record)]
|
||||
Plan(zone, zone, updates, True).raise_if_unsafe()
|
||||
@@ -488,22 +488,23 @@ class TestBaseProvider(TestCase):
|
||||
# MAX_SAFE_UPDATE_PCENT+1 fails when more
|
||||
# than MIN_EXISTING_RECORDS exist
|
||||
zone = Zone('unit.tests.', [])
|
||||
record = Record.new(zone, 'a', {
|
||||
'ttl': 30,
|
||||
'type': 'A',
|
||||
'value': '1.2.3.4',
|
||||
})
|
||||
record = Record.new(
|
||||
zone, 'a', {'ttl': 30, 'type': 'A', 'value': '1.2.3.4'}
|
||||
)
|
||||
|
||||
for i in range(int(Plan.MIN_EXISTING_RECORDS)):
|
||||
zone.add_record(Record.new(zone, str(i), {
|
||||
'ttl': 60,
|
||||
'type': 'A',
|
||||
'value': '2.3.4.5'
|
||||
}))
|
||||
zone.add_record(
|
||||
Record.new(
|
||||
zone, str(i), {'ttl': 60, 'type': 'A', 'value': '2.3.4.5'}
|
||||
)
|
||||
)
|
||||
|
||||
changes = [Update(record, record)
|
||||
for i in range(int(Plan.MIN_EXISTING_RECORDS *
|
||||
Plan.MAX_SAFE_UPDATE_PCENT) + 1)]
|
||||
changes = [
|
||||
Update(record, record)
|
||||
for i in range(
|
||||
int(Plan.MIN_EXISTING_RECORDS * Plan.MAX_SAFE_UPDATE_PCENT) + 1
|
||||
)
|
||||
]
|
||||
|
||||
with self.assertRaises(UnsafePlan) as ctx:
|
||||
Plan(zone, zone, changes, True).raise_if_unsafe()
|
||||
@@ -514,21 +515,22 @@ class TestBaseProvider(TestCase):
|
||||
# MAX_SAFE_UPDATE_PCENT is safe when more
|
||||
# than MIN_EXISTING_RECORDS exist
|
||||
zone = Zone('unit.tests.', [])
|
||||
record = Record.new(zone, 'a', {
|
||||
'ttl': 30,
|
||||
'type': 'A',
|
||||
'value': '1.2.3.4',
|
||||
})
|
||||
record = Record.new(
|
||||
zone, 'a', {'ttl': 30, 'type': 'A', 'value': '1.2.3.4'}
|
||||
)
|
||||
|
||||
for i in range(int(Plan.MIN_EXISTING_RECORDS)):
|
||||
zone.add_record(Record.new(zone, str(i), {
|
||||
'ttl': 60,
|
||||
'type': 'A',
|
||||
'value': '2.3.4.5'
|
||||
}))
|
||||
changes = [Update(record, record)
|
||||
for i in range(int(Plan.MIN_EXISTING_RECORDS *
|
||||
Plan.MAX_SAFE_UPDATE_PCENT))]
|
||||
zone.add_record(
|
||||
Record.new(
|
||||
zone, str(i), {'ttl': 60, 'type': 'A', 'value': '2.3.4.5'}
|
||||
)
|
||||
)
|
||||
changes = [
|
||||
Update(record, record)
|
||||
for i in range(
|
||||
int(Plan.MIN_EXISTING_RECORDS * Plan.MAX_SAFE_UPDATE_PCENT)
|
||||
)
|
||||
]
|
||||
|
||||
Plan(zone, zone, changes, True).raise_if_unsafe()
|
||||
|
||||
@@ -536,22 +538,23 @@ class TestBaseProvider(TestCase):
|
||||
# MAX_SAFE_DELETE_PCENT+1 fails when more
|
||||
# than MIN_EXISTING_RECORDS exist
|
||||
zone = Zone('unit.tests.', [])
|
||||
record = Record.new(zone, 'a', {
|
||||
'ttl': 30,
|
||||
'type': 'A',
|
||||
'value': '1.2.3.4',
|
||||
})
|
||||
record = Record.new(
|
||||
zone, 'a', {'ttl': 30, 'type': 'A', 'value': '1.2.3.4'}
|
||||
)
|
||||
|
||||
for i in range(int(Plan.MIN_EXISTING_RECORDS)):
|
||||
zone.add_record(Record.new(zone, str(i), {
|
||||
'ttl': 60,
|
||||
'type': 'A',
|
||||
'value': '2.3.4.5'
|
||||
}))
|
||||
zone.add_record(
|
||||
Record.new(
|
||||
zone, str(i), {'ttl': 60, 'type': 'A', 'value': '2.3.4.5'}
|
||||
)
|
||||
)
|
||||
|
||||
changes = [Delete(record)
|
||||
for i in range(int(Plan.MIN_EXISTING_RECORDS *
|
||||
Plan.MAX_SAFE_DELETE_PCENT) + 1)]
|
||||
changes = [
|
||||
Delete(record)
|
||||
for i in range(
|
||||
int(Plan.MIN_EXISTING_RECORDS * Plan.MAX_SAFE_DELETE_PCENT) + 1
|
||||
)
|
||||
]
|
||||
|
||||
with self.assertRaises(UnsafePlan) as ctx:
|
||||
Plan(zone, zone, changes, True).raise_if_unsafe()
|
||||
@@ -562,77 +565,78 @@ class TestBaseProvider(TestCase):
|
||||
# MAX_SAFE_DELETE_PCENT is safe when more
|
||||
# than MIN_EXISTING_RECORDS exist
|
||||
zone = Zone('unit.tests.', [])
|
||||
record = Record.new(zone, 'a', {
|
||||
'ttl': 30,
|
||||
'type': 'A',
|
||||
'value': '1.2.3.4',
|
||||
})
|
||||
record = Record.new(
|
||||
zone, 'a', {'ttl': 30, 'type': 'A', 'value': '1.2.3.4'}
|
||||
)
|
||||
|
||||
for i in range(int(Plan.MIN_EXISTING_RECORDS)):
|
||||
zone.add_record(Record.new(zone, str(i), {
|
||||
'ttl': 60,
|
||||
'type': 'A',
|
||||
'value': '2.3.4.5'
|
||||
}))
|
||||
changes = [Delete(record)
|
||||
for i in range(int(Plan.MIN_EXISTING_RECORDS *
|
||||
Plan.MAX_SAFE_DELETE_PCENT))]
|
||||
zone.add_record(
|
||||
Record.new(
|
||||
zone, str(i), {'ttl': 60, 'type': 'A', 'value': '2.3.4.5'}
|
||||
)
|
||||
)
|
||||
changes = [
|
||||
Delete(record)
|
||||
for i in range(
|
||||
int(Plan.MIN_EXISTING_RECORDS * Plan.MAX_SAFE_DELETE_PCENT)
|
||||
)
|
||||
]
|
||||
|
||||
Plan(zone, zone, changes, True).raise_if_unsafe()
|
||||
|
||||
def test_safe_updates_min_existing_override(self):
|
||||
safe_pcent = .4
|
||||
safe_pcent = 0.4
|
||||
# 40% + 1 fails when more
|
||||
# than MIN_EXISTING_RECORDS exist
|
||||
zone = Zone('unit.tests.', [])
|
||||
record = Record.new(zone, 'a', {
|
||||
'ttl': 30,
|
||||
'type': 'A',
|
||||
'value': '1.2.3.4',
|
||||
})
|
||||
record = Record.new(
|
||||
zone, 'a', {'ttl': 30, 'type': 'A', 'value': '1.2.3.4'}
|
||||
)
|
||||
|
||||
for i in range(int(Plan.MIN_EXISTING_RECORDS)):
|
||||
zone.add_record(Record.new(zone, str(i), {
|
||||
'ttl': 60,
|
||||
'type': 'A',
|
||||
'value': '2.3.4.5'
|
||||
}))
|
||||
zone.add_record(
|
||||
Record.new(
|
||||
zone, str(i), {'ttl': 60, 'type': 'A', 'value': '2.3.4.5'}
|
||||
)
|
||||
)
|
||||
|
||||
changes = [Update(record, record)
|
||||
for i in range(int(Plan.MIN_EXISTING_RECORDS *
|
||||
safe_pcent) + 1)]
|
||||
changes = [
|
||||
Update(record, record)
|
||||
for i in range(int(Plan.MIN_EXISTING_RECORDS * safe_pcent) + 1)
|
||||
]
|
||||
|
||||
with self.assertRaises(UnsafePlan) as ctx:
|
||||
Plan(zone, zone, changes, True,
|
||||
update_pcent_threshold=safe_pcent).raise_if_unsafe()
|
||||
Plan(
|
||||
zone, zone, changes, True, update_pcent_threshold=safe_pcent
|
||||
).raise_if_unsafe()
|
||||
|
||||
self.assertTrue('Too many updates' in str(ctx.exception))
|
||||
|
||||
def test_safe_deletes_min_existing_override(self):
|
||||
safe_pcent = .4
|
||||
safe_pcent = 0.4
|
||||
# 40% + 1 fails when more
|
||||
# than MIN_EXISTING_RECORDS exist
|
||||
zone = Zone('unit.tests.', [])
|
||||
record = Record.new(zone, 'a', {
|
||||
'ttl': 30,
|
||||
'type': 'A',
|
||||
'value': '1.2.3.4',
|
||||
})
|
||||
record = Record.new(
|
||||
zone, 'a', {'ttl': 30, 'type': 'A', 'value': '1.2.3.4'}
|
||||
)
|
||||
|
||||
for i in range(int(Plan.MIN_EXISTING_RECORDS)):
|
||||
zone.add_record(Record.new(zone, str(i), {
|
||||
'ttl': 60,
|
||||
'type': 'A',
|
||||
'value': '2.3.4.5'
|
||||
}))
|
||||
zone.add_record(
|
||||
Record.new(
|
||||
zone, str(i), {'ttl': 60, 'type': 'A', 'value': '2.3.4.5'}
|
||||
)
|
||||
)
|
||||
|
||||
changes = [Delete(record)
|
||||
for i in range(int(Plan.MIN_EXISTING_RECORDS *
|
||||
safe_pcent) + 1)]
|
||||
changes = [
|
||||
Delete(record)
|
||||
for i in range(int(Plan.MIN_EXISTING_RECORDS * safe_pcent) + 1)
|
||||
]
|
||||
|
||||
with self.assertRaises(UnsafePlan) as ctx:
|
||||
Plan(zone, zone, changes, True,
|
||||
delete_pcent_threshold=safe_pcent).raise_if_unsafe()
|
||||
Plan(
|
||||
zone, zone, changes, True, delete_pcent_threshold=safe_pcent
|
||||
).raise_if_unsafe()
|
||||
|
||||
self.assertTrue('Too many deletes' in str(ctx.exception))
|
||||
|
||||
@@ -649,9 +653,9 @@ class TestBaseProvider(TestCase):
|
||||
# Should log and not expect
|
||||
normal.supports_warn_or_except('Hello World!', 'Goodbye')
|
||||
normal.log.warning.assert_called_once()
|
||||
normal.log.warning.assert_has_calls([
|
||||
call('%s; %s', 'Hello World!', 'Goodbye')
|
||||
])
|
||||
normal.log.warning.assert_has_calls(
|
||||
[call('%s; %s', 'Hello World!', 'Goodbye')]
|
||||
)
|
||||
|
||||
strict = MinimalProvider(strict_supports=True)
|
||||
# Should log and not expect
|
||||
@@ -662,7 +666,6 @@ class TestBaseProvider(TestCase):
|
||||
|
||||
|
||||
class TestBaseProviderSupportsRootNs(TestCase):
|
||||
|
||||
class Provider(BaseProvider):
|
||||
log = getLogger('Provider')
|
||||
|
||||
@@ -684,33 +687,33 @@ class TestBaseProviderSupportsRootNs(TestCase):
|
||||
return False
|
||||
|
||||
zone = Zone('unit.tests.', [])
|
||||
a_record = Record.new(zone, 'ptr', {
|
||||
'type': 'A',
|
||||
'ttl': 3600,
|
||||
'values': ['1.2.3.4', '2.3.4.5'],
|
||||
})
|
||||
ns_record = Record.new(zone, 'sub', {
|
||||
'type': 'NS',
|
||||
'ttl': 3600,
|
||||
'values': ['ns2.foo.com.', 'ns2.bar.com.'],
|
||||
})
|
||||
a_record = Record.new(
|
||||
zone,
|
||||
'ptr',
|
||||
{'type': 'A', 'ttl': 3600, 'values': ['1.2.3.4', '2.3.4.5']},
|
||||
)
|
||||
ns_record = Record.new(
|
||||
zone,
|
||||
'sub',
|
||||
{'type': 'NS', 'ttl': 3600, 'values': ['ns2.foo.com.', 'ns2.bar.com.']},
|
||||
)
|
||||
no_root = zone.copy()
|
||||
no_root.add_record(a_record)
|
||||
no_root.add_record(ns_record)
|
||||
|
||||
root_ns_record = Record.new(zone, '', {
|
||||
'type': 'NS',
|
||||
'ttl': 3600,
|
||||
'values': ['ns1.foo.com.', 'ns1.bar.com.'],
|
||||
})
|
||||
root_ns_record = Record.new(
|
||||
zone,
|
||||
'',
|
||||
{'type': 'NS', 'ttl': 3600, 'values': ['ns1.foo.com.', 'ns1.bar.com.']},
|
||||
)
|
||||
has_root = no_root.copy()
|
||||
has_root.add_record(root_ns_record)
|
||||
|
||||
other_root_ns_record = Record.new(zone, '', {
|
||||
'type': 'NS',
|
||||
'ttl': 3600,
|
||||
'values': ['ns4.foo.com.', 'ns4.bar.com.'],
|
||||
})
|
||||
other_root_ns_record = Record.new(
|
||||
zone,
|
||||
'',
|
||||
{'type': 'NS', 'ttl': 3600, 'values': ['ns4.foo.com.', 'ns4.bar.com.']},
|
||||
)
|
||||
different_root = no_root.copy()
|
||||
different_root.add_record(other_root_ns_record)
|
||||
|
||||
@@ -733,8 +736,10 @@ class TestBaseProviderSupportsRootNs(TestCase):
|
||||
provider.strict_supports = True
|
||||
with self.assertRaises(SupportsException) as ctx:
|
||||
provider.plan(self.has_root)
|
||||
self.assertEqual('test: root NS record not supported for unit.tests.',
|
||||
str(ctx.exception))
|
||||
self.assertEqual(
|
||||
'test: root NS record not supported for unit.tests.',
|
||||
str(ctx.exception),
|
||||
)
|
||||
|
||||
def test_supports_root_ns_false_different(self):
|
||||
# provider has a non-matching existing record
|
||||
@@ -754,8 +759,10 @@ class TestBaseProviderSupportsRootNs(TestCase):
|
||||
provider.strict_supports = True
|
||||
with self.assertRaises(SupportsException) as ctx:
|
||||
provider.plan(self.has_root)
|
||||
self.assertEqual('test: root NS record not supported for unit.tests.',
|
||||
str(ctx.exception))
|
||||
self.assertEqual(
|
||||
'test: root NS record not supported for unit.tests.',
|
||||
str(ctx.exception),
|
||||
)
|
||||
|
||||
def test_supports_root_ns_false_missing(self):
|
||||
# provider has an existing record
|
||||
@@ -792,8 +799,10 @@ class TestBaseProviderSupportsRootNs(TestCase):
|
||||
provider.strict_supports = True
|
||||
with self.assertRaises(SupportsException) as ctx:
|
||||
provider.plan(self.has_root)
|
||||
self.assertEqual('test: root NS record not supported for unit.tests.',
|
||||
str(ctx.exception))
|
||||
self.assertEqual(
|
||||
'test: root NS record not supported for unit.tests.',
|
||||
str(ctx.exception),
|
||||
)
|
||||
|
||||
def test_supports_root_ns_false_create_zone_missing(self):
|
||||
# provider has no existing records (create)
|
||||
@@ -889,8 +898,9 @@ class TestBaseProviderSupportsRootNs(TestCase):
|
||||
# we'll get a plan that creates everything, including it
|
||||
self.assertTrue(plan)
|
||||
self.assertEqual(3, len(plan.changes))
|
||||
change = [c for c in plan.changes
|
||||
if c.new.name == '' and c.new._type == 'NS'][0]
|
||||
change = [
|
||||
c for c in plan.changes if c.new.name == '' and c.new._type == 'NS'
|
||||
][0]
|
||||
self.assertFalse(change.existing)
|
||||
self.assertEqual(self.root_ns_record, change.new)
|
||||
|
||||
@@ -900,8 +910,9 @@ class TestBaseProviderSupportsRootNs(TestCase):
|
||||
plan = provider.plan(self.has_root)
|
||||
self.assertTrue(plan)
|
||||
self.assertEqual(3, len(plan.changes))
|
||||
change = [c for c in plan.changes
|
||||
if c.new.name == '' and c.new._type == 'NS'][0]
|
||||
change = [
|
||||
c for c in plan.changes if c.new.name == '' and c.new._type == 'NS'
|
||||
][0]
|
||||
self.assertFalse(change.existing)
|
||||
self.assertEqual(self.root_ns_record, change.new)
|
||||
|
||||
|
||||
@@ -2,15 +2,19 @@
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
|
||||
class TestCloudflareShim(TestCase):
|
||||
|
||||
def test_missing(self):
|
||||
with self.assertRaises(ModuleNotFoundError):
|
||||
from octodns.provider.cloudflare import CloudflareProvider
|
||||
|
||||
CloudflareProvider
|
||||
|
||||
@@ -2,15 +2,19 @@
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
|
||||
class TestConstellixShim(TestCase):
|
||||
|
||||
def test_missing(self):
|
||||
with self.assertRaises(ModuleNotFoundError):
|
||||
from octodns.provider.constellix import ConstellixProvider
|
||||
|
||||
ConstellixProvider
|
||||
|
||||
@@ -2,15 +2,19 @@
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
|
||||
class TestDigitalOceanShim(TestCase):
|
||||
|
||||
def test_missing(self):
|
||||
with self.assertRaises(ModuleNotFoundError):
|
||||
from octodns.provider.digitalocean import DigitalOceanProvider
|
||||
|
||||
DigitalOceanProvider
|
||||
|
||||
@@ -2,15 +2,19 @@
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
|
||||
class TestDnsimpleShim(TestCase):
|
||||
|
||||
def test_missing(self):
|
||||
with self.assertRaises(ModuleNotFoundError):
|
||||
from octodns.provider.dnsimple import DnsimpleProvider
|
||||
|
||||
DnsimpleProvider
|
||||
|
||||
@@ -2,15 +2,19 @@
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
|
||||
class TestDnsMadeEasyShim(TestCase):
|
||||
|
||||
def test_missing(self):
|
||||
with self.assertRaises(ModuleNotFoundError):
|
||||
from octodns.provider.dnsmadeeasy import DnsMadeEasyProvider
|
||||
|
||||
DnsMadeEasyProvider
|
||||
|
||||
@@ -2,15 +2,19 @@
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
|
||||
class TestDynShim(TestCase):
|
||||
|
||||
def test_missing(self):
|
||||
with self.assertRaises(ModuleNotFoundError):
|
||||
from octodns.provider.dyn import DynProvider
|
||||
|
||||
DynProvider
|
||||
|
||||
@@ -2,15 +2,19 @@
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
|
||||
class TestEasyDnsShim(TestCase):
|
||||
|
||||
def test_missing(self):
|
||||
with self.assertRaises(ModuleNotFoundError):
|
||||
from octodns.provider.easydns import EasyDnsProvider
|
||||
|
||||
EasyDnsProvider
|
||||
|
||||
@@ -2,20 +2,25 @@
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
# Just for coverage
|
||||
import octodns.provider.fastdns
|
||||
|
||||
# Quell warnings
|
||||
octodns.provider.fastdns
|
||||
|
||||
|
||||
class TestAkamaiShim(TestCase):
|
||||
|
||||
def test_missing(self):
|
||||
with self.assertRaises(ModuleNotFoundError):
|
||||
from octodns.provider.edgedns import AkamaiProvider
|
||||
|
||||
AkamaiProvider
|
||||
|
||||
@@ -2,15 +2,19 @@
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
|
||||
class TestEtcHostsShim(TestCase):
|
||||
|
||||
def test_missing(self):
|
||||
with self.assertRaises(ModuleNotFoundError):
|
||||
from octodns.provider.etc_hosts import EtcHostsProvider
|
||||
|
||||
EtcHostsProvider
|
||||
|
||||
@@ -2,20 +2,25 @@
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
# Just for coverage
|
||||
import octodns.provider.fastdns
|
||||
|
||||
# Quell warnings
|
||||
octodns.provider.fastdns
|
||||
|
||||
|
||||
class TestGandiShim(TestCase):
|
||||
|
||||
def test_missing(self):
|
||||
with self.assertRaises(ModuleNotFoundError):
|
||||
from octodns.provider.gandi import GandiProvider
|
||||
|
||||
GandiProvider
|
||||
|
||||
@@ -2,15 +2,19 @@
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
|
||||
class TestGCoreShim(TestCase):
|
||||
|
||||
def test_missing(self):
|
||||
with self.assertRaises(ModuleNotFoundError):
|
||||
from octodns.provider.gcore import GCoreProvider
|
||||
|
||||
GCoreProvider
|
||||
|
||||
@@ -2,15 +2,19 @@
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
|
||||
class TestGoogleCloudShim(TestCase):
|
||||
|
||||
def test_missing(self):
|
||||
with self.assertRaises(ModuleNotFoundError):
|
||||
from octodns.provider.googlecloud import GoogleCloudProvider
|
||||
|
||||
GoogleCloudProvider
|
||||
|
||||
@@ -2,15 +2,19 @@
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
|
||||
class TestHetznerShim(TestCase):
|
||||
|
||||
def test_missing(self):
|
||||
with self.assertRaises(ModuleNotFoundError):
|
||||
from octodns.provider.hetzner import HetznerProvider
|
||||
|
||||
HetznerProvider
|
||||
|
||||
@@ -2,15 +2,19 @@
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
|
||||
class TestMythicBeastsShim(TestCase):
|
||||
|
||||
def test_missing(self):
|
||||
with self.assertRaises(ModuleNotFoundError):
|
||||
from octodns.provider.mythicbeasts import MythicBeastsProvider
|
||||
|
||||
MythicBeastsProvider
|
||||
|
||||
@@ -2,15 +2,19 @@
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
|
||||
class TestNs1Provider(TestCase):
|
||||
|
||||
def test_missing(self):
|
||||
with self.assertRaises(ModuleNotFoundError):
|
||||
from octodns.provider.ns1 import Ns1Provider
|
||||
|
||||
Ns1Provider
|
||||
|
||||
@@ -2,15 +2,19 @@
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
|
||||
class TestOvhShim(TestCase):
|
||||
|
||||
def test_missing(self):
|
||||
with self.assertRaises(ModuleNotFoundError):
|
||||
from octodns.provider.ovh import OvhProvider
|
||||
|
||||
OvhProvider
|
||||
|
||||
@@ -2,15 +2,19 @@
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
|
||||
class TestPowerDnsShim(TestCase):
|
||||
|
||||
def test_missing(self):
|
||||
with self.assertRaises(ModuleNotFoundError):
|
||||
from octodns.provider.powerdns import PowerDnsProvider
|
||||
|
||||
PowerDnsProvider
|
||||
|
||||
@@ -2,15 +2,19 @@
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
|
||||
class TestRackspaceShim(TestCase):
|
||||
|
||||
def test_missing(self):
|
||||
with self.assertRaises(ModuleNotFoundError):
|
||||
from octodns.provider.rackspace import RackspaceProvider
|
||||
|
||||
RackspaceProvider
|
||||
|
||||
@@ -2,15 +2,19 @@
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
|
||||
class TestRoute53Provider(TestCase):
|
||||
|
||||
def test_missing(self):
|
||||
with self.assertRaises(ModuleNotFoundError):
|
||||
from octodns.provider.route53 import Route53Provider
|
||||
|
||||
Route53Provider
|
||||
|
||||
@@ -2,15 +2,19 @@
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
|
||||
class TestSelectelShim(TestCase):
|
||||
|
||||
def test_missing(self):
|
||||
with self.assertRaises(ModuleNotFoundError):
|
||||
from octodns.provider.selectel import SelectelProvider
|
||||
|
||||
SelectelProvider
|
||||
|
||||
@@ -2,15 +2,19 @@
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
|
||||
class TestTransipShim(TestCase):
|
||||
|
||||
def test_missing(self):
|
||||
with self.assertRaises(ModuleNotFoundError):
|
||||
from octodns.provider.transip import TransipProvider
|
||||
|
||||
TransipProvider
|
||||
|
||||
@@ -2,15 +2,19 @@
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
|
||||
class TestUltraShim(TestCase):
|
||||
|
||||
def test_missing(self):
|
||||
with self.assertRaises(ModuleNotFoundError):
|
||||
from octodns.provider.ultra import UltraProvider
|
||||
|
||||
UltraProvider
|
||||
|
||||
@@ -2,8 +2,12 @@
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
from os import makedirs
|
||||
from os.path import basename, dirname, isdir, isfile, join
|
||||
@@ -13,15 +17,17 @@ from yaml.constructor import ConstructorError
|
||||
|
||||
from octodns.record import Create
|
||||
from octodns.provider.base import Plan
|
||||
from octodns.provider.yaml import _list_all_yaml_files, \
|
||||
SplitYamlProvider, YamlProvider
|
||||
from octodns.provider.yaml import (
|
||||
_list_all_yaml_files,
|
||||
SplitYamlProvider,
|
||||
YamlProvider,
|
||||
)
|
||||
from octodns.zone import SubzoneRecordException, Zone
|
||||
|
||||
from helpers import TemporaryDirectory
|
||||
|
||||
|
||||
class TestYamlProvider(TestCase):
|
||||
|
||||
def test_provider(self):
|
||||
source = YamlProvider('test', join(dirname(__file__), 'config'))
|
||||
|
||||
@@ -57,8 +63,9 @@ class TestYamlProvider(TestCase):
|
||||
|
||||
# We add everything
|
||||
plan = target.plan(zone)
|
||||
self.assertEqual(22, len([c for c in plan.changes
|
||||
if isinstance(c, Create)]))
|
||||
self.assertEqual(
|
||||
22, len([c for c in plan.changes if isinstance(c, Create)])
|
||||
)
|
||||
self.assertFalse(isfile(yaml_file))
|
||||
|
||||
# Now actually do it
|
||||
@@ -67,8 +74,9 @@ class TestYamlProvider(TestCase):
|
||||
|
||||
# Dynamic plan
|
||||
plan = target.plan(dynamic_zone)
|
||||
self.assertEqual(6, len([c for c in plan.changes
|
||||
if isinstance(c, Create)]))
|
||||
self.assertEqual(
|
||||
6, len([c for c in plan.changes if isinstance(c, Create)])
|
||||
)
|
||||
self.assertFalse(isfile(dynamic_yaml_file))
|
||||
# Apply it
|
||||
self.assertEqual(6, target.apply(plan))
|
||||
@@ -79,8 +87,10 @@ class TestYamlProvider(TestCase):
|
||||
target.populate(reloaded)
|
||||
self.assertDictEqual(
|
||||
{'included': ['test']},
|
||||
[x for x in reloaded.records
|
||||
if x.name == 'included'][0]._octodns)
|
||||
[x for x in reloaded.records if x.name == 'included'][
|
||||
0
|
||||
]._octodns,
|
||||
)
|
||||
|
||||
# manually copy over the root since it will have been ignored
|
||||
# when things were written out
|
||||
@@ -90,8 +100,9 @@ class TestYamlProvider(TestCase):
|
||||
|
||||
# A 2nd sync should still create everything
|
||||
plan = target.plan(zone)
|
||||
self.assertEqual(22, len([c for c in plan.changes
|
||||
if isinstance(c, Create)]))
|
||||
self.assertEqual(
|
||||
22, len([c for c in plan.changes if isinstance(c, Create)])
|
||||
)
|
||||
|
||||
with open(yaml_file) as fh:
|
||||
data = safe_load(fh.read())
|
||||
@@ -100,7 +111,7 @@ class TestYamlProvider(TestCase):
|
||||
roots = sorted(data.pop(''), key=lambda r: r['type'])
|
||||
self.assertTrue('values' in roots[0]) # A
|
||||
self.assertTrue('geo' in roots[0]) # geo made the trip
|
||||
self.assertTrue('value' in roots[1]) # CAA
|
||||
self.assertTrue('value' in roots[1]) # CAA
|
||||
self.assertTrue('values' in roots[2]) # SSHFP
|
||||
|
||||
# these are stored as plural 'values'
|
||||
@@ -162,8 +173,9 @@ class TestYamlProvider(TestCase):
|
||||
self.assertEqual([], list(data.keys()))
|
||||
|
||||
def test_empty(self):
|
||||
source = YamlProvider('test', join(dirname(__file__), 'config'),
|
||||
supports_root_ns=False)
|
||||
source = YamlProvider(
|
||||
'test', join(dirname(__file__), 'config'), supports_root_ns=False
|
||||
)
|
||||
|
||||
zone = Zone('empty.', [])
|
||||
|
||||
@@ -172,34 +184,41 @@ class TestYamlProvider(TestCase):
|
||||
self.assertEqual(0, len(zone.records))
|
||||
|
||||
def test_unsorted(self):
|
||||
source = YamlProvider('test', join(dirname(__file__), 'config'),
|
||||
supports_root_ns=False)
|
||||
source = YamlProvider(
|
||||
'test', join(dirname(__file__), 'config'), supports_root_ns=False
|
||||
)
|
||||
|
||||
zone = Zone('unordered.', [])
|
||||
|
||||
with self.assertRaises(ConstructorError):
|
||||
source.populate(zone)
|
||||
|
||||
source = YamlProvider('test', join(dirname(__file__), 'config'),
|
||||
enforce_order=False, supports_root_ns=False)
|
||||
source = YamlProvider(
|
||||
'test',
|
||||
join(dirname(__file__), 'config'),
|
||||
enforce_order=False,
|
||||
supports_root_ns=False,
|
||||
)
|
||||
# no exception
|
||||
source.populate(zone)
|
||||
self.assertEqual(2, len(zone.records))
|
||||
|
||||
def test_subzone_handling(self):
|
||||
source = YamlProvider('test', join(dirname(__file__), 'config'),
|
||||
supports_root_ns=False)
|
||||
source = YamlProvider(
|
||||
'test', join(dirname(__file__), 'config'), supports_root_ns=False
|
||||
)
|
||||
|
||||
# If we add `sub` as a sub-zone we'll reject `www.sub`
|
||||
zone = Zone('unit.tests.', ['sub'])
|
||||
with self.assertRaises(SubzoneRecordException) as ctx:
|
||||
source.populate(zone)
|
||||
self.assertEqual('Record www.sub.unit.tests. is under a managed '
|
||||
'subzone', str(ctx.exception))
|
||||
self.assertEqual(
|
||||
'Record www.sub.unit.tests. is under a managed ' 'subzone',
|
||||
str(ctx.exception),
|
||||
)
|
||||
|
||||
|
||||
class TestSplitYamlProvider(TestCase):
|
||||
|
||||
def test_list_all_yaml_files(self):
|
||||
yaml_files = ('foo.yaml', '1.yaml', '$unit.tests.yaml')
|
||||
all_files = ('something', 'else', '1', '$$', '-f') + yaml_files
|
||||
@@ -223,19 +242,21 @@ class TestSplitYamlProvider(TestCase):
|
||||
|
||||
def test_zone_directory(self):
|
||||
source = SplitYamlProvider(
|
||||
'test', join(dirname(__file__), 'config/split'),
|
||||
extension='.tst')
|
||||
'test', join(dirname(__file__), 'config/split'), extension='.tst'
|
||||
)
|
||||
|
||||
zone = Zone('unit.tests.', [])
|
||||
|
||||
self.assertEqual(
|
||||
join(dirname(__file__), 'config/split', 'unit.tests.tst'),
|
||||
source._zone_directory(zone))
|
||||
source._zone_directory(zone),
|
||||
)
|
||||
|
||||
def test_apply_handles_existing_zone_directory(self):
|
||||
with TemporaryDirectory() as td:
|
||||
provider = SplitYamlProvider('test', join(td.dirname, 'config'),
|
||||
extension='.tst')
|
||||
provider = SplitYamlProvider(
|
||||
'test', join(td.dirname, 'config'), extension='.tst'
|
||||
)
|
||||
makedirs(join(td.dirname, 'config', 'does.exist.tst'))
|
||||
|
||||
zone = Zone('does.exist.', [])
|
||||
@@ -245,8 +266,8 @@ class TestSplitYamlProvider(TestCase):
|
||||
|
||||
def test_provider(self):
|
||||
source = SplitYamlProvider(
|
||||
'test', join(dirname(__file__), 'config/split'),
|
||||
extension='.tst')
|
||||
'test', join(dirname(__file__), 'config/split'), extension='.tst'
|
||||
)
|
||||
|
||||
zone = Zone('unit.tests.', [])
|
||||
dynamic_zone = Zone('dynamic.tests.', [])
|
||||
@@ -267,14 +288,15 @@ class TestSplitYamlProvider(TestCase):
|
||||
directory = join(td.dirname, 'sub', 'dir')
|
||||
zone_dir = join(directory, 'unit.tests.tst')
|
||||
dynamic_zone_dir = join(directory, 'dynamic.tests.tst')
|
||||
target = SplitYamlProvider('test', directory,
|
||||
extension='.tst',
|
||||
supports_root_ns=False)
|
||||
target = SplitYamlProvider(
|
||||
'test', directory, extension='.tst', supports_root_ns=False
|
||||
)
|
||||
|
||||
# We add everything
|
||||
plan = target.plan(zone)
|
||||
self.assertEqual(17, len([c for c in plan.changes
|
||||
if isinstance(c, Create)]))
|
||||
self.assertEqual(
|
||||
17, len([c for c in plan.changes if isinstance(c, Create)])
|
||||
)
|
||||
self.assertFalse(isdir(zone_dir))
|
||||
|
||||
# Now actually do it
|
||||
@@ -282,8 +304,9 @@ class TestSplitYamlProvider(TestCase):
|
||||
|
||||
# Dynamic plan
|
||||
plan = target.plan(dynamic_zone)
|
||||
self.assertEqual(5, len([c for c in plan.changes
|
||||
if isinstance(c, Create)]))
|
||||
self.assertEqual(
|
||||
5, len([c for c in plan.changes if isinstance(c, Create)])
|
||||
)
|
||||
self.assertFalse(isdir(dynamic_zone_dir))
|
||||
# Apply it
|
||||
self.assertEqual(5, target.apply(plan))
|
||||
@@ -294,8 +317,10 @@ class TestSplitYamlProvider(TestCase):
|
||||
target.populate(reloaded)
|
||||
self.assertDictEqual(
|
||||
{'included': ['test']},
|
||||
[x for x in reloaded.records
|
||||
if x.name == 'included'][0]._octodns)
|
||||
[x for x in reloaded.records if x.name == 'included'][
|
||||
0
|
||||
]._octodns,
|
||||
)
|
||||
|
||||
# manually copy over the root since it will have been ignored
|
||||
# when things were written out
|
||||
@@ -305,8 +330,9 @@ class TestSplitYamlProvider(TestCase):
|
||||
|
||||
# A 2nd sync should still create everything
|
||||
plan = target.plan(zone)
|
||||
self.assertEqual(17, len([c for c in plan.changes
|
||||
if isinstance(c, Create)]))
|
||||
self.assertEqual(
|
||||
17, len([c for c in plan.changes if isinstance(c, Create)])
|
||||
)
|
||||
|
||||
yaml_file = join(zone_dir, '$unit.tests.yaml')
|
||||
self.assertTrue(isfile(yaml_file))
|
||||
@@ -315,13 +341,19 @@ class TestSplitYamlProvider(TestCase):
|
||||
roots = sorted(data.pop(''), key=lambda r: r['type'])
|
||||
self.assertTrue('values' in roots[0]) # A
|
||||
self.assertTrue('geo' in roots[0]) # geo made the trip
|
||||
self.assertTrue('value' in roots[1]) # CAA
|
||||
self.assertTrue('value' in roots[1]) # CAA
|
||||
self.assertTrue('values' in roots[2]) # SSHFP
|
||||
|
||||
# These records are stored as plural "values." Check each file to
|
||||
# ensure correctness.
|
||||
for record_name in ('_srv._tcp', 'mx', 'naptr', 'sub', 'txt',
|
||||
'urlfwd'):
|
||||
for record_name in (
|
||||
'_srv._tcp',
|
||||
'mx',
|
||||
'naptr',
|
||||
'sub',
|
||||
'txt',
|
||||
'urlfwd',
|
||||
):
|
||||
yaml_file = join(zone_dir, f'{record_name}.yaml')
|
||||
self.assertTrue(isfile(yaml_file))
|
||||
with open(yaml_file) as fh:
|
||||
@@ -329,8 +361,16 @@ class TestSplitYamlProvider(TestCase):
|
||||
self.assertTrue('values' in data.pop(record_name))
|
||||
|
||||
# These are stored as singular "value." Again, check each file.
|
||||
for record_name in ('aaaa', 'cname', 'dname', 'included', 'ptr',
|
||||
'spf', 'www.sub', 'www'):
|
||||
for record_name in (
|
||||
'aaaa',
|
||||
'cname',
|
||||
'dname',
|
||||
'included',
|
||||
'ptr',
|
||||
'spf',
|
||||
'www.sub',
|
||||
'www',
|
||||
):
|
||||
yaml_file = join(zone_dir, f'{record_name}.yaml')
|
||||
self.assertTrue(isfile(yaml_file))
|
||||
with open(yaml_file) as fh:
|
||||
@@ -339,8 +379,7 @@ class TestSplitYamlProvider(TestCase):
|
||||
|
||||
# Again with the plural, this time checking dynamic.tests.
|
||||
for record_name in ('a', 'aaaa', 'real-ish-a'):
|
||||
yaml_file = join(
|
||||
dynamic_zone_dir, f'{record_name}.yaml')
|
||||
yaml_file = join(dynamic_zone_dir, f'{record_name}.yaml')
|
||||
self.assertTrue(isfile(yaml_file))
|
||||
with open(yaml_file) as fh:
|
||||
data = safe_load(fh.read())
|
||||
@@ -360,8 +399,8 @@ class TestSplitYamlProvider(TestCase):
|
||||
|
||||
def test_empty(self):
|
||||
source = SplitYamlProvider(
|
||||
'test', join(dirname(__file__), 'config/split'),
|
||||
extension='.tst')
|
||||
'test', join(dirname(__file__), 'config/split'), extension='.tst'
|
||||
)
|
||||
|
||||
zone = Zone('empty.', [])
|
||||
|
||||
@@ -371,8 +410,8 @@ class TestSplitYamlProvider(TestCase):
|
||||
|
||||
def test_unsorted(self):
|
||||
source = SplitYamlProvider(
|
||||
'test', join(dirname(__file__), 'config/split'),
|
||||
extension='.tst')
|
||||
'test', join(dirname(__file__), 'config/split'), extension='.tst'
|
||||
)
|
||||
|
||||
zone = Zone('unordered.', [])
|
||||
|
||||
@@ -382,35 +421,46 @@ class TestSplitYamlProvider(TestCase):
|
||||
zone = Zone('unordered.', [])
|
||||
|
||||
source = SplitYamlProvider(
|
||||
'test', join(dirname(__file__), 'config/split'),
|
||||
extension='.tst', enforce_order=False)
|
||||
'test',
|
||||
join(dirname(__file__), 'config/split'),
|
||||
extension='.tst',
|
||||
enforce_order=False,
|
||||
)
|
||||
# no exception
|
||||
source.populate(zone)
|
||||
self.assertEqual(2, len(zone.records))
|
||||
|
||||
def test_subzone_handling(self):
|
||||
source = SplitYamlProvider(
|
||||
'test', join(dirname(__file__), 'config/split'),
|
||||
extension='.tst')
|
||||
'test', join(dirname(__file__), 'config/split'), extension='.tst'
|
||||
)
|
||||
|
||||
# If we add `sub` as a sub-zone we'll reject `www.sub`
|
||||
zone = Zone('unit.tests.', ['sub'])
|
||||
with self.assertRaises(SubzoneRecordException) as ctx:
|
||||
source.populate(zone)
|
||||
self.assertEqual('Record www.sub.unit.tests. is under a managed '
|
||||
'subzone', str(ctx.exception))
|
||||
self.assertEqual(
|
||||
'Record www.sub.unit.tests. is under a managed ' 'subzone',
|
||||
str(ctx.exception),
|
||||
)
|
||||
|
||||
|
||||
class TestOverridingYamlProvider(TestCase):
|
||||
|
||||
def test_provider(self):
|
||||
config = join(dirname(__file__), 'config')
|
||||
override_config = join(dirname(__file__), 'config', 'override')
|
||||
base = YamlProvider('base', config, populate_should_replace=False,
|
||||
supports_root_ns=False)
|
||||
override = YamlProvider('test', override_config,
|
||||
populate_should_replace=True,
|
||||
supports_root_ns=False)
|
||||
base = YamlProvider(
|
||||
'base',
|
||||
config,
|
||||
populate_should_replace=False,
|
||||
supports_root_ns=False,
|
||||
)
|
||||
override = YamlProvider(
|
||||
'test',
|
||||
override_config,
|
||||
populate_should_replace=True,
|
||||
supports_root_ns=False,
|
||||
)
|
||||
|
||||
zone = Zone('dynamic.tests.', [])
|
||||
|
||||
@@ -428,9 +478,8 @@ class TestOverridingYamlProvider(TestCase):
|
||||
got = {r.name: r for r in zone.records}
|
||||
self.assertEqual(7, len(got))
|
||||
# 'a' was replaced with a generic record
|
||||
self.assertEqual({
|
||||
'ttl': 3600,
|
||||
'values': ['4.4.4.4', '5.5.5.5']
|
||||
}, got['a'].data)
|
||||
self.assertEqual(
|
||||
{'ttl': 3600, 'values': ['4.4.4.4', '5.5.5.5']}, got['a'].data
|
||||
)
|
||||
# And we have the new one
|
||||
self.assertTrue('added' in got)
|
||||
|
||||
+2906
-3002
File diff suppressed because it is too large
Load Diff
@@ -2,8 +2,12 @@
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
@@ -11,7 +15,6 @@ from octodns.record.geo import GeoCodes
|
||||
|
||||
|
||||
class TestRecordGeoCodes(TestCase):
|
||||
|
||||
def test_validate(self):
|
||||
prefix = 'xyz '
|
||||
|
||||
@@ -21,53 +24,81 @@ class TestRecordGeoCodes(TestCase):
|
||||
self.assertEqual([], GeoCodes.validate('NA-US-OR', prefix))
|
||||
|
||||
# Just plain bad
|
||||
self.assertEqual(['xyz invalid geo code "XX-YY-ZZ-AA"'],
|
||||
GeoCodes.validate('XX-YY-ZZ-AA', prefix))
|
||||
self.assertEqual(['xyz unknown continent code "X-Y-Z"'],
|
||||
GeoCodes.validate('X-Y-Z', prefix))
|
||||
self.assertEqual(['xyz unknown continent code "XXX-Y-Z"'],
|
||||
GeoCodes.validate('XXX-Y-Z', prefix))
|
||||
self.assertEqual(
|
||||
['xyz invalid geo code "XX-YY-ZZ-AA"'],
|
||||
GeoCodes.validate('XX-YY-ZZ-AA', prefix),
|
||||
)
|
||||
self.assertEqual(
|
||||
['xyz unknown continent code "X-Y-Z"'],
|
||||
GeoCodes.validate('X-Y-Z', prefix),
|
||||
)
|
||||
self.assertEqual(
|
||||
['xyz unknown continent code "XXX-Y-Z"'],
|
||||
GeoCodes.validate('XXX-Y-Z', prefix),
|
||||
)
|
||||
|
||||
# Bad continent
|
||||
self.assertEqual(['xyz unknown continent code "XX"'],
|
||||
GeoCodes.validate('XX', prefix))
|
||||
self.assertEqual(
|
||||
['xyz unknown continent code "XX"'], GeoCodes.validate('XX', prefix)
|
||||
)
|
||||
# Bad continent good country
|
||||
self.assertEqual(['xyz unknown continent code "XX-US"'],
|
||||
GeoCodes.validate('XX-US', prefix))
|
||||
self.assertEqual(
|
||||
['xyz unknown continent code "XX-US"'],
|
||||
GeoCodes.validate('XX-US', prefix),
|
||||
)
|
||||
# Bad continent good country and province
|
||||
self.assertEqual(['xyz unknown continent code "XX-US-OR"'],
|
||||
GeoCodes.validate('XX-US-OR', prefix))
|
||||
self.assertEqual(
|
||||
['xyz unknown continent code "XX-US-OR"'],
|
||||
GeoCodes.validate('XX-US-OR', prefix),
|
||||
)
|
||||
|
||||
# Bad country, good continent
|
||||
self.assertEqual(['xyz unknown country code "NA-XX"'],
|
||||
GeoCodes.validate('NA-XX', prefix))
|
||||
self.assertEqual(
|
||||
['xyz unknown country code "NA-XX"'],
|
||||
GeoCodes.validate('NA-XX', prefix),
|
||||
)
|
||||
# Bad country, good continent and state
|
||||
self.assertEqual(['xyz unknown country code "NA-XX-OR"'],
|
||||
GeoCodes.validate('NA-XX-OR', prefix))
|
||||
self.assertEqual(
|
||||
['xyz unknown country code "NA-XX-OR"'],
|
||||
GeoCodes.validate('NA-XX-OR', prefix),
|
||||
)
|
||||
# Good country, good continent, but bad match
|
||||
self.assertEqual(['xyz unknown country code "NA-GB"'],
|
||||
GeoCodes.validate('NA-GB', prefix))
|
||||
self.assertEqual(
|
||||
['xyz unknown country code "NA-GB"'],
|
||||
GeoCodes.validate('NA-GB', prefix),
|
||||
)
|
||||
|
||||
# Bad province code, good continent and country
|
||||
self.assertEqual(['xyz unknown province code "NA-US-XX"'],
|
||||
GeoCodes.validate('NA-US-XX', prefix))
|
||||
self.assertEqual(
|
||||
['xyz unknown province code "NA-US-XX"'],
|
||||
GeoCodes.validate('NA-US-XX', prefix),
|
||||
)
|
||||
|
||||
def test_parse(self):
|
||||
self.assertEqual({
|
||||
'continent_code': 'NA',
|
||||
'country_code': None,
|
||||
'province_code': None,
|
||||
}, GeoCodes.parse('NA'))
|
||||
self.assertEqual({
|
||||
'continent_code': 'NA',
|
||||
'country_code': 'US',
|
||||
'province_code': None,
|
||||
}, GeoCodes.parse('NA-US'))
|
||||
self.assertEqual({
|
||||
'continent_code': 'NA',
|
||||
'country_code': 'US',
|
||||
'province_code': 'CA',
|
||||
}, GeoCodes.parse('NA-US-CA'))
|
||||
self.assertEqual(
|
||||
{
|
||||
'continent_code': 'NA',
|
||||
'country_code': None,
|
||||
'province_code': None,
|
||||
},
|
||||
GeoCodes.parse('NA'),
|
||||
)
|
||||
self.assertEqual(
|
||||
{
|
||||
'continent_code': 'NA',
|
||||
'country_code': 'US',
|
||||
'province_code': None,
|
||||
},
|
||||
GeoCodes.parse('NA-US'),
|
||||
)
|
||||
self.assertEqual(
|
||||
{
|
||||
'continent_code': 'NA',
|
||||
'country_code': 'US',
|
||||
'province_code': 'CA',
|
||||
},
|
||||
GeoCodes.parse('NA-US-CA'),
|
||||
)
|
||||
|
||||
def test_country_to_code(self):
|
||||
self.assertEqual('NA-US', GeoCodes.country_to_code('US'))
|
||||
|
||||
@@ -2,8 +2,12 @@
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
import dns.zone
|
||||
from dns.exception import DNSException
|
||||
@@ -13,8 +17,12 @@ from shutil import copyfile
|
||||
from unittest import TestCase
|
||||
from unittest.mock import patch
|
||||
|
||||
from octodns.source.axfr import AxfrSource, AxfrSourceZoneTransferFailed, \
|
||||
ZoneFileSource, ZoneFileSourceLoadFailure
|
||||
from octodns.source.axfr import (
|
||||
AxfrSource,
|
||||
AxfrSourceZoneTransferFailed,
|
||||
ZoneFileSource,
|
||||
ZoneFileSourceLoadFailure,
|
||||
)
|
||||
from octodns.zone import Zone
|
||||
from octodns.record import ValidationError
|
||||
|
||||
@@ -22,17 +30,15 @@ from octodns.record import ValidationError
|
||||
class TestAxfrSource(TestCase):
|
||||
source = AxfrSource('test', 'localhost')
|
||||
|
||||
forward_zonefile = dns.zone.from_file('./tests/zones/unit.tests.tst',
|
||||
'unit.tests', relativize=False)
|
||||
forward_zonefile = dns.zone.from_file(
|
||||
'./tests/zones/unit.tests.tst', 'unit.tests', relativize=False
|
||||
)
|
||||
|
||||
@patch('dns.zone.from_xfr')
|
||||
def test_populate(self, from_xfr_mock):
|
||||
got = Zone('unit.tests.', [])
|
||||
|
||||
from_xfr_mock.side_effect = [
|
||||
self.forward_zonefile,
|
||||
DNSException
|
||||
]
|
||||
from_xfr_mock.side_effect = [self.forward_zonefile, DNSException]
|
||||
|
||||
self.source.populate(got)
|
||||
self.assertEqual(16, len(got.records))
|
||||
@@ -40,8 +46,7 @@ class TestAxfrSource(TestCase):
|
||||
with self.assertRaises(AxfrSourceZoneTransferFailed) as ctx:
|
||||
zone = Zone('unit.tests.', [])
|
||||
self.source.populate(zone)
|
||||
self.assertEqual('Unable to Perform Zone Transfer',
|
||||
str(ctx.exception))
|
||||
self.assertEqual('Unable to Perform Zone Transfer', str(ctx.exception))
|
||||
|
||||
|
||||
class TestZoneFileSource(TestCase):
|
||||
@@ -65,8 +70,10 @@ class TestZoneFileSource(TestCase):
|
||||
# It did so we need to skip this test, that means windows won't
|
||||
# have full code coverage, but skipping the test is going out of
|
||||
# our way enough for a os-specific/oddball case.
|
||||
self.skipTest('Unable to create unit.tests. (ending with .) so '
|
||||
'skipping default filename testing.')
|
||||
self.skipTest(
|
||||
'Unable to create unit.tests. (ending with .) so '
|
||||
'skipping default filename testing.'
|
||||
)
|
||||
|
||||
source = ZoneFileSource('test', './tests/zones')
|
||||
# Load zonefiles without a specified file extension
|
||||
@@ -97,16 +104,19 @@ class TestZoneFileSource(TestCase):
|
||||
with self.assertRaises(ZoneFileSourceLoadFailure) as ctx:
|
||||
zone = Zone('invalid.zone.', [])
|
||||
self.source.populate(zone)
|
||||
self.assertEqual('The DNS zone has no NS RRset at its origin.',
|
||||
str(ctx.exception))
|
||||
self.assertEqual(
|
||||
'The DNS zone has no NS RRset at its origin.', str(ctx.exception)
|
||||
)
|
||||
|
||||
# Records are not to RFC (lenient=False)
|
||||
with self.assertRaises(ValidationError) as ctx:
|
||||
zone = Zone('invalid.records.', [])
|
||||
self.source.populate(zone)
|
||||
self.assertEqual('Invalid record _invalid.invalid.records.\n'
|
||||
' - invalid name for SRV record',
|
||||
str(ctx.exception))
|
||||
self.assertEqual(
|
||||
'Invalid record _invalid.invalid.records.\n'
|
||||
' - invalid name for SRV record',
|
||||
str(ctx.exception),
|
||||
)
|
||||
|
||||
# Records are not to RFC, but load anyhow (lenient=True)
|
||||
invalid = Zone('invalid.records.', [])
|
||||
|
||||
@@ -7,7 +7,6 @@ from octodns.zone import Zone
|
||||
|
||||
|
||||
class TestEnvVarSource(TestCase):
|
||||
|
||||
def test_read_variable(self):
|
||||
envvar = 'OCTODNS_TEST_ENVIRONMENT_VARIABLE'
|
||||
source = EnvVarSource('testid', envvar, 'recordname', ttl=120)
|
||||
|
||||
@@ -2,8 +2,12 @@
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
@@ -24,103 +28,96 @@ class TestTinyDnsFileSource(TestCase):
|
||||
|
||||
expected = Zone('example.com.', [])
|
||||
for name, data in (
|
||||
('', {
|
||||
'type': 'A',
|
||||
'ttl': 30,
|
||||
'values': ['10.2.3.4', '10.2.3.5'],
|
||||
}),
|
||||
('', {
|
||||
'type': 'NS',
|
||||
'ttl': 3600,
|
||||
'values': ['ns1.ns.com.', 'ns2.ns.com.'],
|
||||
}),
|
||||
('sub', {
|
||||
'type': 'NS',
|
||||
'ttl': 30,
|
||||
'values': ['ns3.ns.com.', 'ns4.ns.com.'],
|
||||
}),
|
||||
('www', {
|
||||
'type': 'A',
|
||||
'ttl': 3600,
|
||||
'value': '10.2.3.6',
|
||||
}),
|
||||
('cname', {
|
||||
'type': 'CNAME',
|
||||
'ttl': 3600,
|
||||
'value': 'www.example.com.',
|
||||
}),
|
||||
('some-host-abc123', {
|
||||
'type': 'A',
|
||||
'ttl': 1800,
|
||||
'value': '10.2.3.7',
|
||||
}),
|
||||
('has-dup-def123', {
|
||||
'type': 'A',
|
||||
'ttl': 3600,
|
||||
'value': '10.2.3.8',
|
||||
}),
|
||||
('www.sub', {
|
||||
'type': 'A',
|
||||
'ttl': 3600,
|
||||
'value': '1.2.3.4',
|
||||
}),
|
||||
('has-dup-def456', {
|
||||
'type': 'A',
|
||||
'ttl': 3600,
|
||||
'value': '10.2.3.8',
|
||||
}),
|
||||
('', {
|
||||
'type': 'MX',
|
||||
'ttl': 3600,
|
||||
'values': [{
|
||||
'preference': 10,
|
||||
'exchange': 'smtp-1-host.example.com.',
|
||||
}, {
|
||||
'preference': 20,
|
||||
'exchange': 'smtp-2-host.example.com.',
|
||||
}]
|
||||
}),
|
||||
('smtp', {
|
||||
'type': 'MX',
|
||||
'ttl': 1800,
|
||||
'values': [{
|
||||
'preference': 30,
|
||||
'exchange': 'smtp-1-host.example.com.',
|
||||
}, {
|
||||
'preference': 40,
|
||||
'exchange': 'smtp-2-host.example.com.',
|
||||
}]
|
||||
}),
|
||||
('', {
|
||||
'type': 'TXT',
|
||||
'ttl': 300,
|
||||
'value': 'test TXT',
|
||||
}),
|
||||
('colon', {
|
||||
'type': 'TXT',
|
||||
'ttl': 300,
|
||||
'value': 'test : TXT',
|
||||
}),
|
||||
('nottl', {
|
||||
'type': 'TXT',
|
||||
'ttl': 3600,
|
||||
'value': 'nottl test TXT',
|
||||
}),
|
||||
('ipv6-3', {
|
||||
'type': 'AAAA',
|
||||
'ttl': 300,
|
||||
'value': '2a02:1348:017c:d5d0:0024:19ff:fef3:5742',
|
||||
}),
|
||||
('ipv6-6', {
|
||||
'type': 'AAAA',
|
||||
'ttl': 3600,
|
||||
'value': '2a02:1348:017c:d5d0:0024:19ff:fef3:5743',
|
||||
}),
|
||||
('semicolon', {
|
||||
'type': 'TXT',
|
||||
'ttl': 300,
|
||||
'value': 'v=DKIM1\\; k=rsa\\; p=blah',
|
||||
}),
|
||||
('', {'type': 'A', 'ttl': 30, 'values': ['10.2.3.4', '10.2.3.5']}),
|
||||
(
|
||||
'',
|
||||
{
|
||||
'type': 'NS',
|
||||
'ttl': 3600,
|
||||
'values': ['ns1.ns.com.', 'ns2.ns.com.'],
|
||||
},
|
||||
),
|
||||
(
|
||||
'sub',
|
||||
{
|
||||
'type': 'NS',
|
||||
'ttl': 30,
|
||||
'values': ['ns3.ns.com.', 'ns4.ns.com.'],
|
||||
},
|
||||
),
|
||||
('www', {'type': 'A', 'ttl': 3600, 'value': '10.2.3.6'}),
|
||||
(
|
||||
'cname',
|
||||
{'type': 'CNAME', 'ttl': 3600, 'value': 'www.example.com.'},
|
||||
),
|
||||
(
|
||||
'some-host-abc123',
|
||||
{'type': 'A', 'ttl': 1800, 'value': '10.2.3.7'},
|
||||
),
|
||||
('has-dup-def123', {'type': 'A', 'ttl': 3600, 'value': '10.2.3.8'}),
|
||||
('www.sub', {'type': 'A', 'ttl': 3600, 'value': '1.2.3.4'}),
|
||||
('has-dup-def456', {'type': 'A', 'ttl': 3600, 'value': '10.2.3.8'}),
|
||||
(
|
||||
'',
|
||||
{
|
||||
'type': 'MX',
|
||||
'ttl': 3600,
|
||||
'values': [
|
||||
{
|
||||
'preference': 10,
|
||||
'exchange': 'smtp-1-host.example.com.',
|
||||
},
|
||||
{
|
||||
'preference': 20,
|
||||
'exchange': 'smtp-2-host.example.com.',
|
||||
},
|
||||
],
|
||||
},
|
||||
),
|
||||
(
|
||||
'smtp',
|
||||
{
|
||||
'type': 'MX',
|
||||
'ttl': 1800,
|
||||
'values': [
|
||||
{
|
||||
'preference': 30,
|
||||
'exchange': 'smtp-1-host.example.com.',
|
||||
},
|
||||
{
|
||||
'preference': 40,
|
||||
'exchange': 'smtp-2-host.example.com.',
|
||||
},
|
||||
],
|
||||
},
|
||||
),
|
||||
('', {'type': 'TXT', 'ttl': 300, 'value': 'test TXT'}),
|
||||
('colon', {'type': 'TXT', 'ttl': 300, 'value': 'test : TXT'}),
|
||||
('nottl', {'type': 'TXT', 'ttl': 3600, 'value': 'nottl test TXT'}),
|
||||
(
|
||||
'ipv6-3',
|
||||
{
|
||||
'type': 'AAAA',
|
||||
'ttl': 300,
|
||||
'value': '2a02:1348:017c:d5d0:0024:19ff:fef3:5742',
|
||||
},
|
||||
),
|
||||
(
|
||||
'ipv6-6',
|
||||
{
|
||||
'type': 'AAAA',
|
||||
'ttl': 3600,
|
||||
'value': '2a02:1348:017c:d5d0:0024:19ff:fef3:5743',
|
||||
},
|
||||
),
|
||||
(
|
||||
'semicolon',
|
||||
{
|
||||
'type': 'TXT',
|
||||
'ttl': 300,
|
||||
'value': 'v=DKIM1\\; k=rsa\\; p=blah',
|
||||
},
|
||||
),
|
||||
):
|
||||
record = Record.new(expected, name, data)
|
||||
expected.add_record(record)
|
||||
@@ -135,11 +132,7 @@ class TestTinyDnsFileSource(TestCase):
|
||||
|
||||
expected = Zone('asdf.subtest.com.', [])
|
||||
for name, data in (
|
||||
('a3', {
|
||||
'type': 'A',
|
||||
'ttl': 3600,
|
||||
'values': ['10.2.3.7'],
|
||||
}),
|
||||
('a3', {'type': 'A', 'ttl': 3600, 'values': ['10.2.3.7']}),
|
||||
):
|
||||
record = Record.new(expected, name, data)
|
||||
expected.add_record(record)
|
||||
@@ -154,16 +147,8 @@ class TestTinyDnsFileSource(TestCase):
|
||||
|
||||
expected = Zone('sub-asdf.subtest.com.', [])
|
||||
for name, data in (
|
||||
('a1', {
|
||||
'type': 'A',
|
||||
'ttl': 3600,
|
||||
'values': ['10.2.3.5'],
|
||||
}),
|
||||
('a2', {
|
||||
'type': 'A',
|
||||
'ttl': 3600,
|
||||
'values': ['10.2.3.6'],
|
||||
}),
|
||||
('a1', {'type': 'A', 'ttl': 3600, 'values': ['10.2.3.5']}),
|
||||
('a2', {'type': 'A', 'ttl': 3600, 'values': ['10.2.3.6']}),
|
||||
):
|
||||
record = Record.new(expected, name, data)
|
||||
expected.add_record(record)
|
||||
@@ -178,26 +163,24 @@ class TestTinyDnsFileSource(TestCase):
|
||||
|
||||
expected = Zone('3.2.10.in-addr.arpa.', [])
|
||||
for name, data in (
|
||||
('10', {
|
||||
'type': 'PTR',
|
||||
'ttl': 3600,
|
||||
'value': 'a-ptr.example.com.'
|
||||
}),
|
||||
('11', {
|
||||
'type': 'PTR',
|
||||
'ttl': 30,
|
||||
'value': 'a-ptr-2.example.com.'
|
||||
}),
|
||||
('8', {
|
||||
'type': 'PTR',
|
||||
'ttl': 3600,
|
||||
'value': 'has-dup-def123.example.com.'
|
||||
}),
|
||||
('7', {
|
||||
'type': 'PTR',
|
||||
'ttl': 1800,
|
||||
'value': 'some-host-abc123.example.com.'
|
||||
}),
|
||||
('10', {'type': 'PTR', 'ttl': 3600, 'value': 'a-ptr.example.com.'}),
|
||||
('11', {'type': 'PTR', 'ttl': 30, 'value': 'a-ptr-2.example.com.'}),
|
||||
(
|
||||
'8',
|
||||
{
|
||||
'type': 'PTR',
|
||||
'ttl': 3600,
|
||||
'value': 'has-dup-def123.example.com.',
|
||||
},
|
||||
),
|
||||
(
|
||||
'7',
|
||||
{
|
||||
'type': 'PTR',
|
||||
'ttl': 1800,
|
||||
'value': 'some-host-abc123.example.com.',
|
||||
},
|
||||
),
|
||||
):
|
||||
record = Record.new(expected, name, data)
|
||||
expected.add_record(record)
|
||||
|
||||
+33
-33
@@ -2,8 +2,12 @@
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
from io import StringIO
|
||||
from unittest import TestCase
|
||||
@@ -13,58 +17,54 @@ from octodns.yaml import safe_dump, safe_load
|
||||
|
||||
|
||||
class TestYaml(TestCase):
|
||||
|
||||
def test_stuff(self):
|
||||
self.assertEqual({
|
||||
1: 'a',
|
||||
2: 'b',
|
||||
'3': 'c',
|
||||
10: 'd',
|
||||
'11': 'e',
|
||||
}, safe_load('''
|
||||
self.assertEqual(
|
||||
{1: 'a', 2: 'b', '3': 'c', 10: 'd', '11': 'e'},
|
||||
safe_load(
|
||||
'''
|
||||
1: a
|
||||
2: b
|
||||
'3': c
|
||||
10: d
|
||||
'11': e
|
||||
'''))
|
||||
'''
|
||||
),
|
||||
)
|
||||
|
||||
self.assertEqual({
|
||||
'*.1.2': 'a',
|
||||
'*.2.2': 'b',
|
||||
'*.10.1': 'c',
|
||||
'*.11.2': 'd',
|
||||
}, safe_load('''
|
||||
self.assertEqual(
|
||||
{'*.1.2': 'a', '*.2.2': 'b', '*.10.1': 'c', '*.11.2': 'd'},
|
||||
safe_load(
|
||||
'''
|
||||
'*.1.2': 'a'
|
||||
'*.2.2': 'b'
|
||||
'*.10.1': 'c'
|
||||
'*.11.2': 'd'
|
||||
'''))
|
||||
'''
|
||||
),
|
||||
)
|
||||
|
||||
with self.assertRaises(ConstructorError) as ctx:
|
||||
safe_load('''
|
||||
safe_load(
|
||||
'''
|
||||
'*.2.2': 'b'
|
||||
'*.1.2': 'a'
|
||||
'*.11.2': 'd'
|
||||
'*.10.1': 'c'
|
||||
''')
|
||||
self.assertTrue('keys out of order: expected *.1.2 got *.2.2 at' in
|
||||
ctx.exception.problem)
|
||||
'''
|
||||
)
|
||||
self.assertTrue(
|
||||
'keys out of order: expected *.1.2 got *.2.2 at'
|
||||
in ctx.exception.problem
|
||||
)
|
||||
|
||||
buf = StringIO()
|
||||
safe_dump({
|
||||
'*.1.1': 42,
|
||||
'*.11.1': 43,
|
||||
'*.2.1': 44,
|
||||
}, buf)
|
||||
self.assertEqual("---\n'*.1.1': 42\n'*.2.1': 44\n'*.11.1': 43\n",
|
||||
buf.getvalue())
|
||||
safe_dump({'*.1.1': 42, '*.11.1': 43, '*.2.1': 44}, buf)
|
||||
self.assertEqual(
|
||||
"---\n'*.1.1': 42\n'*.2.1': 44\n'*.11.1': 43\n", buf.getvalue()
|
||||
)
|
||||
|
||||
# hex sorting isn't ideal, not treated as hex, this make sure we don't
|
||||
# change the behavior
|
||||
buf = StringIO()
|
||||
safe_dump({
|
||||
'45a03129': 42,
|
||||
'45a0392a': 43,
|
||||
}, buf)
|
||||
safe_dump({'45a03129': 42, '45a0392a': 43}, buf)
|
||||
self.assertEqual("---\n45a0392a: 43\n45a03129: 42\n", buf.getvalue())
|
||||
|
||||
+108
-96
@@ -2,21 +2,35 @@
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
from octodns.record import ARecord, AaaaRecord, Create, Delete, NsRecord, \
|
||||
Record, Update
|
||||
from octodns.zone import DuplicateRecordException, InvalidNodeException, \
|
||||
SubzoneRecordException, Zone
|
||||
from octodns.record import (
|
||||
ARecord,
|
||||
AaaaRecord,
|
||||
Create,
|
||||
Delete,
|
||||
NsRecord,
|
||||
Record,
|
||||
Update,
|
||||
)
|
||||
from octodns.zone import (
|
||||
DuplicateRecordException,
|
||||
InvalidNodeException,
|
||||
SubzoneRecordException,
|
||||
Zone,
|
||||
)
|
||||
|
||||
from helpers import SimpleProvider
|
||||
|
||||
|
||||
class TestZone(TestCase):
|
||||
|
||||
def test_lowering(self):
|
||||
zone = Zone('UniT.TEsTs.', [])
|
||||
self.assertEqual('unit.tests.', zone.name)
|
||||
@@ -47,8 +61,9 @@ class TestZone(TestCase):
|
||||
# Can't add record with same name & type
|
||||
with self.assertRaises(DuplicateRecordException) as ctx:
|
||||
zone.add_record(a)
|
||||
self.assertEqual('Duplicate record a.unit.tests., type A',
|
||||
str(ctx.exception))
|
||||
self.assertEqual(
|
||||
'Duplicate record a.unit.tests., type A', str(ctx.exception)
|
||||
)
|
||||
self.assertEqual(zone.records, set([a]))
|
||||
|
||||
# can add duplicate with replace=True
|
||||
@@ -108,7 +123,6 @@ class TestZone(TestCase):
|
||||
update.__repr__()
|
||||
|
||||
def test_unsupporting(self):
|
||||
|
||||
class NoAaaaProvider(object):
|
||||
id = 'no-aaaa'
|
||||
SUPPORTS_GEO = False
|
||||
@@ -144,21 +158,21 @@ class TestZone(TestCase):
|
||||
|
||||
# NS for exactly the sub is allowed
|
||||
zone = Zone('unit.tests.', set(['sub', 'barred']))
|
||||
record = Record.new(zone, 'sub', {
|
||||
'ttl': 3600,
|
||||
'type': 'NS',
|
||||
'values': ['1.2.3.4.', '2.3.4.5.'],
|
||||
})
|
||||
record = Record.new(
|
||||
zone,
|
||||
'sub',
|
||||
{'ttl': 3600, 'type': 'NS', 'values': ['1.2.3.4.', '2.3.4.5.']},
|
||||
)
|
||||
zone.add_record(record)
|
||||
self.assertEqual(set([record]), zone.records)
|
||||
|
||||
# non-NS for exactly the sub is rejected
|
||||
zone = Zone('unit.tests.', set(['sub', 'barred']))
|
||||
record = Record.new(zone, 'sub', {
|
||||
'ttl': 3600,
|
||||
'type': 'A',
|
||||
'values': ['1.2.3.4', '2.3.4.5'],
|
||||
})
|
||||
record = Record.new(
|
||||
zone,
|
||||
'sub',
|
||||
{'ttl': 3600, 'type': 'A', 'values': ['1.2.3.4', '2.3.4.5']},
|
||||
)
|
||||
with self.assertRaises(SubzoneRecordException) as ctx:
|
||||
zone.add_record(record)
|
||||
self.assertTrue('not of type NS', str(ctx.exception))
|
||||
@@ -168,11 +182,11 @@ class TestZone(TestCase):
|
||||
|
||||
# NS for something below the sub is rejected
|
||||
zone = Zone('unit.tests.', set(['sub', 'barred']))
|
||||
record = Record.new(zone, 'foo.sub', {
|
||||
'ttl': 3600,
|
||||
'type': 'NS',
|
||||
'values': ['1.2.3.4.', '2.3.4.5.'],
|
||||
})
|
||||
record = Record.new(
|
||||
zone,
|
||||
'foo.sub',
|
||||
{'ttl': 3600, 'type': 'NS', 'values': ['1.2.3.4.', '2.3.4.5.']},
|
||||
)
|
||||
with self.assertRaises(SubzoneRecordException) as ctx:
|
||||
zone.add_record(record)
|
||||
self.assertTrue('under a managed sub-zone', str(ctx.exception))
|
||||
@@ -182,11 +196,11 @@ class TestZone(TestCase):
|
||||
|
||||
# A for something below the sub is rejected
|
||||
zone = Zone('unit.tests.', set(['sub', 'barred']))
|
||||
record = Record.new(zone, 'foo.bar.sub', {
|
||||
'ttl': 3600,
|
||||
'type': 'A',
|
||||
'values': ['1.2.3.4', '2.3.4.5'],
|
||||
})
|
||||
record = Record.new(
|
||||
zone,
|
||||
'foo.bar.sub',
|
||||
{'ttl': 3600, 'type': 'A', 'values': ['1.2.3.4', '2.3.4.5']},
|
||||
)
|
||||
with self.assertRaises(SubzoneRecordException) as ctx:
|
||||
zone.add_record(record)
|
||||
self.assertTrue('under a managed sub-zone', str(ctx.exception))
|
||||
@@ -199,21 +213,21 @@ class TestZone(TestCase):
|
||||
zone_ignored = Zone('unit.tests.', [])
|
||||
zone_missing = Zone('unit.tests.', [])
|
||||
|
||||
normal = Record.new(zone_normal, 'www', {
|
||||
'ttl': 60,
|
||||
'type': 'A',
|
||||
'value': '9.9.9.9',
|
||||
})
|
||||
normal = Record.new(
|
||||
zone_normal, 'www', {'ttl': 60, 'type': 'A', 'value': '9.9.9.9'}
|
||||
)
|
||||
zone_normal.add_record(normal)
|
||||
|
||||
ignored = Record.new(zone_ignored, 'www', {
|
||||
'octodns': {
|
||||
'ignored': True
|
||||
ignored = Record.new(
|
||||
zone_ignored,
|
||||
'www',
|
||||
{
|
||||
'octodns': {'ignored': True},
|
||||
'ttl': 60,
|
||||
'type': 'A',
|
||||
'value': '9.9.9.9',
|
||||
},
|
||||
'ttl': 60,
|
||||
'type': 'A',
|
||||
'value': '9.9.9.9',
|
||||
})
|
||||
)
|
||||
zone_ignored.add_record(ignored)
|
||||
|
||||
provider = SimpleProvider()
|
||||
@@ -229,16 +243,12 @@ class TestZone(TestCase):
|
||||
|
||||
def test_cname_coexisting(self):
|
||||
zone = Zone('unit.tests.', [])
|
||||
a = Record.new(zone, 'www', {
|
||||
'ttl': 60,
|
||||
'type': 'A',
|
||||
'value': '9.9.9.9',
|
||||
})
|
||||
cname = Record.new(zone, 'www', {
|
||||
'ttl': 60,
|
||||
'type': 'CNAME',
|
||||
'value': 'foo.bar.com.',
|
||||
})
|
||||
a = Record.new(
|
||||
zone, 'www', {'ttl': 60, 'type': 'A', 'value': '9.9.9.9'}
|
||||
)
|
||||
cname = Record.new(
|
||||
zone, 'www', {'ttl': 60, 'type': 'CNAME', 'value': 'foo.bar.com.'}
|
||||
)
|
||||
|
||||
# add cname to a
|
||||
zone.add_record(a)
|
||||
@@ -262,21 +272,21 @@ class TestZone(TestCase):
|
||||
zone_excluded = Zone('unit.tests.', [])
|
||||
zone_missing = Zone('unit.tests.', [])
|
||||
|
||||
normal = Record.new(zone_normal, 'www', {
|
||||
'ttl': 60,
|
||||
'type': 'A',
|
||||
'value': '9.9.9.9',
|
||||
})
|
||||
normal = Record.new(
|
||||
zone_normal, 'www', {'ttl': 60, 'type': 'A', 'value': '9.9.9.9'}
|
||||
)
|
||||
zone_normal.add_record(normal)
|
||||
|
||||
excluded = Record.new(zone_excluded, 'www', {
|
||||
'octodns': {
|
||||
'excluded': ['test']
|
||||
excluded = Record.new(
|
||||
zone_excluded,
|
||||
'www',
|
||||
{
|
||||
'octodns': {'excluded': ['test']},
|
||||
'ttl': 60,
|
||||
'type': 'A',
|
||||
'value': '9.9.9.9',
|
||||
},
|
||||
'ttl': 60,
|
||||
'type': 'A',
|
||||
'value': '9.9.9.9',
|
||||
})
|
||||
)
|
||||
zone_excluded.add_record(excluded)
|
||||
|
||||
provider = SimpleProvider()
|
||||
@@ -295,21 +305,21 @@ class TestZone(TestCase):
|
||||
zone_included = Zone('unit.tests.', [])
|
||||
zone_missing = Zone('unit.tests.', [])
|
||||
|
||||
normal = Record.new(zone_normal, 'www', {
|
||||
'ttl': 60,
|
||||
'type': 'A',
|
||||
'value': '9.9.9.9',
|
||||
})
|
||||
normal = Record.new(
|
||||
zone_normal, 'www', {'ttl': 60, 'type': 'A', 'value': '9.9.9.9'}
|
||||
)
|
||||
zone_normal.add_record(normal)
|
||||
|
||||
included = Record.new(zone_included, 'www', {
|
||||
'octodns': {
|
||||
'included': ['test']
|
||||
included = Record.new(
|
||||
zone_included,
|
||||
'www',
|
||||
{
|
||||
'octodns': {'included': ['test']},
|
||||
'ttl': 60,
|
||||
'type': 'A',
|
||||
'value': '9.9.9.9',
|
||||
},
|
||||
'ttl': 60,
|
||||
'type': 'A',
|
||||
'value': '9.9.9.9',
|
||||
})
|
||||
)
|
||||
zone_included.add_record(included)
|
||||
|
||||
provider = SimpleProvider()
|
||||
@@ -328,21 +338,21 @@ class TestZone(TestCase):
|
||||
zone_included = Zone('unit.tests.', [])
|
||||
zone_missing = Zone('unit.tests.', [])
|
||||
|
||||
normal = Record.new(zone_normal, 'www', {
|
||||
'ttl': 60,
|
||||
'type': 'A',
|
||||
'value': '9.9.9.9',
|
||||
})
|
||||
normal = Record.new(
|
||||
zone_normal, 'www', {'ttl': 60, 'type': 'A', 'value': '9.9.9.9'}
|
||||
)
|
||||
zone_normal.add_record(normal)
|
||||
|
||||
included = Record.new(zone_included, 'www', {
|
||||
'octodns': {
|
||||
'included': ['not-here']
|
||||
included = Record.new(
|
||||
zone_included,
|
||||
'www',
|
||||
{
|
||||
'octodns': {'included': ['not-here']},
|
||||
'ttl': 60,
|
||||
'type': 'A',
|
||||
'value': '9.9.9.9',
|
||||
},
|
||||
'ttl': 60,
|
||||
'type': 'A',
|
||||
'value': '9.9.9.9',
|
||||
})
|
||||
)
|
||||
zone_included.add_record(included)
|
||||
|
||||
provider = SimpleProvider()
|
||||
@@ -420,18 +430,20 @@ class TestZone(TestCase):
|
||||
# No root NS yet
|
||||
self.assertFalse(zone.root_ns)
|
||||
|
||||
non_root_ns = NsRecord(zone, 'sub', {'ttl': 42, 'values': (
|
||||
'ns1.unit.tests.',
|
||||
'ns2.unit.tests.',
|
||||
)})
|
||||
non_root_ns = NsRecord(
|
||||
zone,
|
||||
'sub',
|
||||
{'ttl': 42, 'values': ('ns1.unit.tests.', 'ns2.unit.tests.')},
|
||||
)
|
||||
zone.add_record(non_root_ns)
|
||||
# No root NS yet b/c this was a sub
|
||||
self.assertFalse(zone.root_ns)
|
||||
|
||||
root_ns = NsRecord(zone, '', {'ttl': 42, 'values': (
|
||||
'ns3.unit.tests.',
|
||||
'ns4.unit.tests.',
|
||||
)})
|
||||
root_ns = NsRecord(
|
||||
zone,
|
||||
'',
|
||||
{'ttl': 42, 'values': ('ns3.unit.tests.', 'ns4.unit.tests.')},
|
||||
)
|
||||
zone.add_record(root_ns)
|
||||
# Now we have a root NS
|
||||
self.assertEqual(root_ns, zone.root_ns)
|
||||
|
||||
Reference in New Issue
Block a user