1
0
mirror of https://github.com/github/octodns.git synced 2024-05-11 05:55:00 +00:00
Files
Ross McFarland 2a99bd922f Always add ownership flatting records with lenient=True
This is intened to address cases where NS delegation ownership records
are added to the parent zone when they technically should live in the
delegated child. They're only used as ~metadata so this is fine, so long
as the provider supports it.
2023-06-20 07:41:38 -07:00

108 lines
3.7 KiB
Python

#
#
#
from collections import defaultdict
from ..provider.plan import Plan
from ..record import Record
from .base import BaseProcessor
# Mark anything octoDNS is managing that way it can know it's safe to modify or
# delete. We'll take ownership of existing records that we're told to manage
# and thus "own" them going forward.
class OwnershipProcessor(BaseProcessor):
def __init__(self, name, txt_name='_owner', txt_value='*octodns*'):
super().__init__(name)
self.txt_name = txt_name
self.txt_value = txt_value
self._txt_values = [txt_value]
def process_source_zone(self, desired, *args, **kwargs):
for record in desired.records:
# Then create and add an ownership TXT for each of them
record_name = record.name.replace('*', '_wildcard')
if record.name:
name = f'{self.txt_name}.{record._type}.{record_name}'
else:
name = f'{self.txt_name}.{record._type}'
txt = Record.new(
desired,
name,
{'type': 'TXT', 'ttl': 60, 'value': self.txt_value},
)
# add these w/lenient to cover the case when the ownership record
# for a NS delegation record should technically live in the subzone
desired.add_record(txt, lenient=True)
return desired
def _is_ownership(self, record):
return (
record._type == 'TXT'
and record.name.startswith(self.txt_name)
and record.values == self._txt_values
)
def process_plan(self, plan, *args, **kwargs):
if not plan:
# If we don't have any change there's nothing to do
return plan
# First find all the ownership info
owned = defaultdict(dict)
# We need to look for ownership in both the desired and existing
# states, many things will show up in both, but that's fine.
for record in list(plan.existing.records) + list(plan.desired.records):
if self._is_ownership(record):
pieces = record.name.split('.', 2)
if len(pieces) > 2:
_, _type, name = pieces
name = name.replace('_wildcard', '*')
else:
_type = pieces[1]
name = ''
owned[name][_type.upper()] = True
# Cases:
# - Configured in source
# - We'll fully CRU/manage it adding ownership TXT,
# thanks to process_source_zone, if needed
# - Not in source
# - Has an ownership TXT - delete it & the ownership TXT
# - Does not have an ownership TXT - don't delete it
# - Special records like octodns-meta
# - Should be left alone and should not have ownerthis TXTs
filtered_changes = []
for change in plan.changes:
record = change.record
if (
not self._is_ownership(record)
and record._type not in owned[record.name]
and record.name != 'octodns-meta'
):
# It's not an ownership TXT, it's not owned, and it's not
# special we're going to ignore it
continue
# We own this record or owned it up until now so whatever the
# change is we should do
filtered_changes.append(change)
if not filtered_changes:
return None
elif plan.changes != filtered_changes:
return Plan(
plan.existing,
plan.desired,
filtered_changes,
plan.exists,
plan.update_pcent_threshold,
plan.delete_pcent_threshold,
)
return plan