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

Merge branch 'master' into idna

This commit is contained in:
Ross McFarland
2022-07-04 10:20:48 -07:00
committed by GitHub
8 changed files with 59 additions and 48 deletions

3
.github/FUNDING.yml vendored
View File

@@ -1,3 +0,0 @@
# These are supported funding model platforms
github: ross

View File

@@ -162,23 +162,21 @@ class Manager(object):
processor_name) processor_name)
zone_tree = {} zone_tree = {}
# sort by reversed strings so that parent zones always come first # Sort so we iterate on the deepest nodes first, ensuring if a parent
for name in sorted(self.config['zones'].keys(), key=lambda s: s[::-1]): # zone exists it will be seen after the subzone, thus we can easily
# ignore trailing dots, and reverse # reparent children to their parent zone from the tree root.
pieces = name[:-1].split('.')[::-1] for name in sorted(self.config['zones'].keys(),
# where starts out at the top key=lambda s: 0 - s.count('.')):
where = zone_tree # Trim the trailing dot from FQDN
# for all the pieces name = name[:-1]
for piece in pieces: this = {}
try: for sz in [k for k in zone_tree.keys() if k.endswith(name)]:
where = where[piece] # Found a zone in tree root that is our child, slice the
# our current piece already exists, just point where at # name and move its tree under ours.
# it's value this[sz[:-(len(name) + 1)]] = zone_tree.pop(sz)
except KeyError: # Add to tree root where it will be reparented as we iterate up
# our current piece doesn't exist, create it # the tree.
where[piece] = {} zone_tree[name] = this
# and then point where at it's newly created value
where = where[piece]
self.zone_tree = zone_tree self.zone_tree = zone_tree
self.plan_outputs = {} self.plan_outputs = {}
@@ -274,21 +272,20 @@ class Manager(object):
return kwargs return kwargs
def configured_sub_zones(self, zone_name): def configured_sub_zones(self, zone_name):
# Reversed pieces of the zone name name = zone_name[:-1]
pieces = zone_name[:-1].split('.')[::-1]
# Point where at the root of the tree
where = self.zone_tree where = self.zone_tree
# Until we've hit the bottom of this zone while True:
try: # Find parent if it exists
while pieces: parent = next((k for k in where if name.endswith(k)), None)
# Point where at the value of our current piece if not parent:
where = where[pieces.pop(0)] # The zone_name in the tree has been reached, stop searching.
except KeyError: break
self.log.debug('configured_sub_zones: unknown zone, %s, no subs', # Move down the tree and slice name to get the remainder for the
zone_name) # next round of the search.
return set() where = where[parent]
# We're not pointed at the dict for our name, the keys of which will be name = name[:-(len(parent) + 1)]
# any subzones # `where` is now pointing at the dictionary of children for zone_name
# in the tree
sub_zone_names = where.keys() sub_zone_names = where.keys()
self.log.debug('configured_sub_zones: subs=%s', sub_zone_names) self.log.debug('configured_sub_zones: subs=%s', sub_zone_names)
return set(sub_zone_names) return set(sub_zone_names)

View File

@@ -68,10 +68,9 @@ class Zone(object):
self.hydrate() self.hydrate()
name = record.name name = record.name
last = name.split('.')[-1]
if not lenient and last in self.sub_zones: if not lenient and any((name.endswith(sz) for sz in self.sub_zones)):
if name != last: if name not in self.sub_zones:
# it's a record for something under a sub-zone # it's a record for something under a sub-zone
raise SubzoneRecordException(f'Record {record.fqdn} is under ' raise SubzoneRecordException(f'Record {record.fqdn} is under '
'a managed subzone') 'a managed subzone')

View File

@@ -33,6 +33,11 @@ zones:
targets: targets:
- dump - dump
- dump2 - dump2
sub.txt.unit.tests.:
sources:
- in
targets:
- dump
empty.: empty.:
sources: sources:
- in - in

View File

@@ -0,0 +1 @@
---

View File

@@ -162,6 +162,16 @@ sub:
values: values:
- 6.2.3.4. - 6.2.3.4.
- 7.2.3.4. - 7.2.3.4.
sub.txt:
type: 'NS'
values:
- ns1.test.
- ns2.test.
subzone:
type: 'NS'
values:
- 192.0.2.1.
- 192.0.2.8.
txt: txt:
ttl: 600 ttl: 600
type: TXT type: TXT

View File

@@ -121,12 +121,12 @@ class TestManager(TestCase):
environ['YAML_TMP_DIR'] = tmpdir.dirname environ['YAML_TMP_DIR'] = tmpdir.dirname
tc = Manager(get_config_filename('simple.yaml')) \ tc = Manager(get_config_filename('simple.yaml')) \
.sync(dry_run=False) .sync(dry_run=False)
self.assertEqual(26, tc) self.assertEqual(28, tc)
# try with just one of the zones # try with just one of the zones
tc = Manager(get_config_filename('simple.yaml')) \ tc = Manager(get_config_filename('simple.yaml')) \
.sync(dry_run=False, eligible_zones=['unit.tests.']) .sync(dry_run=False, eligible_zones=['unit.tests.'])
self.assertEqual(20, tc) self.assertEqual(22, tc)
# the subzone, with 2 targets # the subzone, with 2 targets
tc = Manager(get_config_filename('simple.yaml')) \ tc = Manager(get_config_filename('simple.yaml')) \
@@ -141,18 +141,18 @@ class TestManager(TestCase):
# Again with force # Again with force
tc = Manager(get_config_filename('simple.yaml')) \ tc = Manager(get_config_filename('simple.yaml')) \
.sync(dry_run=False, force=True) .sync(dry_run=False, force=True)
self.assertEqual(26, tc) self.assertEqual(28, tc)
# Again with max_workers = 1 # Again with max_workers = 1
tc = Manager(get_config_filename('simple.yaml'), max_workers=1) \ tc = Manager(get_config_filename('simple.yaml'), max_workers=1) \
.sync(dry_run=False, force=True) .sync(dry_run=False, force=True)
self.assertEqual(26, tc) self.assertEqual(28, tc)
# Include meta # Include meta
tc = Manager(get_config_filename('simple.yaml'), max_workers=1, tc = Manager(get_config_filename('simple.yaml'), max_workers=1,
include_meta=True) \ include_meta=True) \
.sync(dry_run=False, force=True) .sync(dry_run=False, force=True)
self.assertEqual(30, tc) self.assertEqual(33, tc)
def test_eligible_sources(self): def test_eligible_sources(self):
with TemporaryDirectory() as tmpdir: with TemporaryDirectory() as tmpdir:
@@ -220,13 +220,13 @@ class TestManager(TestCase):
# compare doesn't use _process_desired_zone and thus doesn't filter # compare doesn't use _process_desired_zone and thus doesn't filter
# out root NS records, that seems fine/desirable # out root NS records, that seems fine/desirable
changes = manager.compare(['in'], ['dump'], 'unit.tests.') changes = manager.compare(['in'], ['dump'], 'unit.tests.')
self.assertEqual(21, len(changes)) self.assertEqual(23, len(changes))
# Compound sources with varying support # Compound sources with varying support
changes = manager.compare(['in', 'nosshfp'], changes = manager.compare(['in', 'nosshfp'],
['dump'], ['dump'],
'unit.tests.') 'unit.tests.')
self.assertEqual(20, len(changes)) self.assertEqual(22, len(changes))
with self.assertRaises(ManagerException) as ctx: with self.assertRaises(ManagerException) as ctx:
manager.compare(['nope'], ['dump'], 'unit.tests.') manager.compare(['nope'], ['dump'], 'unit.tests.')

View File

@@ -34,7 +34,7 @@ class TestYamlProvider(TestCase):
# without it we see everything # without it we see everything
source.populate(zone) source.populate(zone)
self.assertEqual(23, len(zone.records)) self.assertEqual(25, len(zone.records))
source.populate(dynamic_zone) source.populate(dynamic_zone)
self.assertEqual(6, len(dynamic_zone.records)) self.assertEqual(6, len(dynamic_zone.records))
@@ -57,12 +57,12 @@ class TestYamlProvider(TestCase):
# We add everything # We add everything
plan = target.plan(zone) plan = target.plan(zone)
self.assertEqual(20, len([c for c in plan.changes self.assertEqual(22, len([c for c in plan.changes
if isinstance(c, Create)])) if isinstance(c, Create)]))
self.assertFalse(isfile(yaml_file)) self.assertFalse(isfile(yaml_file))
# Now actually do it # Now actually do it
self.assertEqual(20, target.apply(plan)) self.assertEqual(22, target.apply(plan))
self.assertTrue(isfile(yaml_file)) self.assertTrue(isfile(yaml_file))
# Dynamic plan # Dynamic plan
@@ -90,7 +90,7 @@ class TestYamlProvider(TestCase):
# A 2nd sync should still create everything # A 2nd sync should still create everything
plan = target.plan(zone) plan = target.plan(zone)
self.assertEqual(20, len([c for c in plan.changes self.assertEqual(22, len([c for c in plan.changes
if isinstance(c, Create)])) if isinstance(c, Create)]))
with open(yaml_file) as fh: with open(yaml_file) as fh:
@@ -111,6 +111,8 @@ class TestYamlProvider(TestCase):
self.assertTrue('values' in data.pop('txt')) self.assertTrue('values' in data.pop('txt'))
self.assertTrue('values' in data.pop('loc')) self.assertTrue('values' in data.pop('loc'))
self.assertTrue('values' in data.pop('urlfwd')) self.assertTrue('values' in data.pop('urlfwd'))
self.assertTrue('values' in data.pop('sub.txt'))
self.assertTrue('values' in data.pop('subzone'))
# these are stored as singular 'value' # these are stored as singular 'value'
self.assertTrue('value' in data.pop('_imap._tcp')) self.assertTrue('value' in data.pop('_imap._tcp'))
self.assertTrue('value' in data.pop('_pop3._tcp')) self.assertTrue('value' in data.pop('_pop3._tcp'))