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

Support dotted subdomains for subzones

Currently if there are two zones configured;
- example.com.
- delegated.subdomain.example.com.

When an NS record is created in example.com.yaml as such:
delegated.subdomain:
  type: NS
  values:
  - ns1.example.org.

The NS record for delegated.subdomain.example.com cannot be created as it
throws an exception:

octodns.zone.SubzoneRecordException: Record delegated.subdomain.example.com is under a managed subzone

Additionally, all records other than NS are rejected for
subdomain.example.com..

This is caused by zone_tree being the result of all zones split on '.'
and being added to the tree, even if a zone does not exist at that
point.

To support records where a subzone is dotted, the the map is built such
that each node represents the subdomain of the closest subzone.

Before:
{"com", {"example": {"subdomain": {"delegated": {}}}}}

After:
{"example.com": {"delegated.subdomain": {}}}

Fixes: #378
This commit is contained in:
Sachi King
2022-06-23 20:31:49 +10:00
parent e299ceced2
commit 5592f5da96
2 changed files with 22 additions and 34 deletions

View File

@@ -162,23 +162,18 @@ class Manager(object):
processor_name)
zone_tree = {}
# sort by reversed strings so that parent zones always come first
for name in sorted(self.config['zones'].keys(), key=lambda s: s[::-1]):
# ignore trailing dots, and reverse
pieces = name[:-1].split('.')[::-1]
# where starts out at the top
where = zone_tree
# for all the pieces
for piece in pieces:
try:
where = where[piece]
# our current piece already exists, just point where at
# it's value
except KeyError:
# our current piece doesn't exist, create it
where[piece] = {}
# and then point where at it's newly created value
where = where[piece]
# Sort so we iterate on the deepest nodes first, ensuring if a parent
# zone exists it will be seen after the subzone, thus we can easily
# reparent children to their parent zone from the tree root.
for name in sorted(self.config['zones'].keys(),
key=lambda s: 0 - s.count('.')):
name = name[:-1]
this = {}
for sz in filter(
lambda k: k.endswith(name), set(zone_tree.keys())
):
this[sz[:-(len(name) + 1)]] = zone_tree.pop(sz)
zone_tree[name] = this
self.zone_tree = zone_tree
self.plan_outputs = {}
@@ -274,21 +269,14 @@ class Manager(object):
return kwargs
def configured_sub_zones(self, zone_name):
# Reversed pieces of the zone name
pieces = zone_name[:-1].split('.')[::-1]
# Point where at the root of the tree
name = zone_name[:-1]
where = self.zone_tree
# Until we've hit the bottom of this zone
try:
while pieces:
# Point where at the value of our current piece
where = where[pieces.pop(0)]
except KeyError:
self.log.debug('configured_sub_zones: unknown zone, %s, no subs',
zone_name)
return set()
# We're not pointed at the dict for our name, the keys of which will be
# any subzones
while True:
parent = next(filter(lambda k: name.endswith(k), where), None)
if not parent:
break
where = where[parent]
name = name[:-(len(parent) + 1)]
sub_zone_names = where.keys()
self.log.debug('configured_sub_zones: subs=%s', sub_zone_names)
return set(sub_zone_names)

View File

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