From 974d047d6ee1ffb38d2d9366c10161800769730c Mon Sep 17 00:00:00 2001 From: Ross McFarland Date: Mon, 28 Jul 2025 18:19:45 -0700 Subject: [PATCH] Fix issue with using Templating processor on aliased zones Switches the processor to use process_source_and_target_zones which happens later, during planning changes and thus happens seperately for the source and aliased zones. This leaves the templates in the copy of desired that Manager uses to start the alias process. The updated copy is in the plan so externally everything will still make sense and behave as expected. --- .../519279fa6fa94b2fb61bd3552084dbd6.md | 4 ++ octodns/processor/templating.py | 8 +-- tests/test_octodns_processor_templating.py | 61 +++---------------- 3 files changed, 14 insertions(+), 59 deletions(-) create mode 100644 .changelog/519279fa6fa94b2fb61bd3552084dbd6.md diff --git a/.changelog/519279fa6fa94b2fb61bd3552084dbd6.md b/.changelog/519279fa6fa94b2fb61bd3552084dbd6.md new file mode 100644 index 0000000..c264805 --- /dev/null +++ b/.changelog/519279fa6fa94b2fb61bd3552084dbd6.md @@ -0,0 +1,4 @@ +--- +type: minor +--- +Fix issues with using Templating processor on alias zones \ No newline at end of file diff --git a/octodns/processor/templating.py b/octodns/processor/templating.py index c514bec..2551e32 100644 --- a/octodns/processor/templating.py +++ b/octodns/processor/templating.py @@ -59,19 +59,17 @@ class Templating(BaseProcessor): super().__init__(id, *args, **kwargs) self.context = context - def process_source_zone(self, desired, sources): - sources = sources or [] + def process_source_and_target_zones(self, desired, existing, provider): zone_params = { 'zone_name': desired.decoded_name, 'zone_decoded_name': desired.decoded_name, 'zone_encoded_name': desired.name, 'zone_num_records': len(desired.records), - 'zone_source_ids': ', '.join(s.id for s in sources), # add any extra context provided to us, if the value is a callable # object call it passing our params so that arbitrary dynamic # context can be added for use in formatting **{ - k: (v(desired, sources) if callable(v) else v) + k: (v(desired, provider) if callable(v) else v) for k, v in self.context.items() }, } @@ -110,4 +108,4 @@ class Templating(BaseProcessor): new.value = new_value desired.add_record(new, replace=True) - return desired + return desired, existing diff --git a/tests/test_octodns_processor_templating.py b/tests/test_octodns_processor_templating.py index 94472d8..20cc1b4 100644 --- a/tests/test_octodns_processor_templating.py +++ b/tests/test_octodns_processor_templating.py @@ -3,7 +3,6 @@ # from unittest import TestCase -from unittest.mock import call, patch from octodns.processor.templating import Templating from octodns.record import Record, ValueMixin, ValuesMixin @@ -91,7 +90,7 @@ class TemplatingTest(TestCase): ) zone.add_record(noop) - got = templ.process_source_zone(zone, None) + got, _ = templ.process_source_and_target_zones(zone, None, None) cname = _find(got, 'cname') self.assertEqual('_cname.unit.tests.something.else.', cname.value) noop = _find(got, 'noop') @@ -118,7 +117,7 @@ class TemplatingTest(TestCase): ) zone.add_record(noop) - got = templ.process_source_zone(zone, None) + got, _ = templ.process_source_and_target_zones(zone, None, None) txt = _find(got, 'txt') self.assertEqual('There are 2 record(s) in unit.tests.', txt.values[0]) noop = _find(got, 'noop') @@ -138,56 +137,12 @@ class TemplatingTest(TestCase): # this should check for the template method on our values that don't # have one - templ.process_source_zone(zone, None) + templ.process_source_and_target_zones(zone, None, None) # and these should make sure that the value types were asked if they # have a template method self.assertEqual({'template'}, s.value._asked_for) self.assertEqual({'template'}, m.values[0]._asked_for) - @patch('octodns.record.TxtValue.template') - def test_params(self, mock_template): - templ = Templating('test') - - zone = Zone('unit.tests.', []) - record_source = DummySource('record') - txt = Record.new( - zone, - 'txt', - { - 'type': 'TXT', - 'ttl': 42, - 'value': 'There are {zone_num_records} record(s) in {zone_name}', - }, - source=record_source, - ) - zone.add_record(txt) - - templ.process_source_zone( - zone, sources=[record_source, DummySource('other')] - ) - mock_template.assert_called_once() - self.assertEqual( - call( - { - 'record_name': 'txt', - 'record_decoded_name': 'txt', - 'record_encoded_name': 'txt', - 'record_fqdn': 'txt.unit.tests.', - 'record_decoded_fqdn': 'txt.unit.tests.', - 'record_encoded_fqdn': 'txt.unit.tests.', - 'record_type': 'TXT', - 'record_ttl': 42, - 'record_source_id': 'record', - 'zone_name': 'unit.tests.', - 'zone_decoded_name': 'unit.tests.', - 'zone_encoded_name': 'unit.tests.', - 'zone_num_records': 1, - 'zone_source_ids': 'record, other', - } - ), - mock_template.call_args, - ) - def test_context(self): templ = Templating( 'test', @@ -197,7 +152,7 @@ class TemplatingTest(TestCase): # dynamic 'the_date': lambda _, __: 'today', # uses a param - 'num_sources': lambda z, ss: len(ss), + 'provider': lambda _, pro: pro, }, ) @@ -211,17 +166,15 @@ class TemplatingTest(TestCase): 'values': ( 'the_answer: {the_answer}', 'the_date: {the_date}', - 'num_sources: {num_sources}', + 'provider: {provider}', ), }, ) zone.add_record(txt) - got = templ.process_source_zone( - zone, tuple(DummySource(i) for i in range(3)) - ) + got, _ = templ.process_source_and_target_zones(zone, None, 'da-pro') txt = _find(got, 'txt') self.assertEqual(3, len(txt.values)) - self.assertEqual('num_sources: 3', txt.values[0]) + self.assertEqual('provider: da-pro', txt.values[0]) self.assertEqual('the_answer: 42', txt.values[1]) self.assertEqual('the_date: today', txt.values[2])