From d75e672fe1bf7d9e99ec5994e7114efbaeba46dc Mon Sep 17 00:00:00 2001 From: Jonathan Leroy Date: Mon, 28 Jul 2025 15:02:03 +0200 Subject: [PATCH 1/6] Removes the final dot from zone_*name and record_*fqdn templating variables --- .changelog/6c6407b8ddf345ecb2e850e34d1ce328.md | 4 ++++ octodns/processor/templating.py | 12 ++++++------ tests/test_octodns_processor_templating.py | 18 +++++++++--------- 3 files changed, 19 insertions(+), 15 deletions(-) create mode 100644 .changelog/6c6407b8ddf345ecb2e850e34d1ce328.md diff --git a/.changelog/6c6407b8ddf345ecb2e850e34d1ce328.md b/.changelog/6c6407b8ddf345ecb2e850e34d1ce328.md new file mode 100644 index 0000000..4f36618 --- /dev/null +++ b/.changelog/6c6407b8ddf345ecb2e850e34d1ce328.md @@ -0,0 +1,4 @@ +--- +type: patch +--- +Removes the final dot from zone_*name and record_*fqdn templating variables \ No newline at end of file diff --git a/octodns/processor/templating.py b/octodns/processor/templating.py index c514bec..6477872 100644 --- a/octodns/processor/templating.py +++ b/octodns/processor/templating.py @@ -62,9 +62,9 @@ class Templating(BaseProcessor): def process_source_zone(self, desired, sources): sources = sources or [] zone_params = { - 'zone_name': desired.decoded_name, - 'zone_decoded_name': desired.decoded_name, - 'zone_encoded_name': desired.name, + 'zone_name': desired.decoded_name.rstrip('.'), + 'zone_decoded_name': desired.decoded_name.rstrip('.'), + 'zone_encoded_name': desired.name.rstrip('.'), '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 @@ -81,9 +81,9 @@ class Templating(BaseProcessor): 'record_name': record.decoded_name, 'record_decoded_name': record.decoded_name, 'record_encoded_name': record.name, - 'record_fqdn': record.decoded_fqdn, - 'record_decoded_fqdn': record.decoded_fqdn, - 'record_encoded_fqdn': record.fqdn, + 'record_fqdn': record.decoded_fqdn.rstrip('.'), + 'record_decoded_fqdn': record.decoded_fqdn.rstrip('.'), + 'record_encoded_fqdn': record.fqdn.rstrip('.'), 'record_type': record._type, 'record_ttl': record.ttl, 'record_source_id': record.source.id if record.source else None, diff --git a/tests/test_octodns_processor_templating.py b/tests/test_octodns_processor_templating.py index 94472d8..7c90706 100644 --- a/tests/test_octodns_processor_templating.py +++ b/tests/test_octodns_processor_templating.py @@ -74,7 +74,7 @@ class TemplatingTest(TestCase): { 'type': 'CNAME', 'ttl': 42, - 'value': '_cname.{zone_name}something.else.', + 'value': '_cname.{zone_name}.something.else.', }, lenient=True, ) @@ -107,7 +107,7 @@ class TemplatingTest(TestCase): { 'type': 'TXT', 'ttl': 42, - 'value': 'There are {zone_num_records} record(s) in {zone_name}', + 'value': 'There are {zone_num_records} record(s) in {zone_name}.', }, ) zone.add_record(txt) @@ -156,7 +156,7 @@ class TemplatingTest(TestCase): { 'type': 'TXT', 'ttl': 42, - 'value': 'There are {zone_num_records} record(s) in {zone_name}', + 'value': 'There are {zone_num_records} record(s) in {zone_name}.', }, source=record_source, ) @@ -172,15 +172,15 @@ class TemplatingTest(TestCase): '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_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_name': 'unit.tests', + 'zone_decoded_name': 'unit.tests', + 'zone_encoded_name': 'unit.tests', 'zone_num_records': 1, 'zone_source_ids': 'record, other', } From 3e7cca0c6c135a031325a33868433eec81916fe2 Mon Sep 17 00:00:00 2001 From: Jonathan Leroy Date: Mon, 28 Jul 2025 21:01:45 +0200 Subject: [PATCH 2/6] Fix Templating processor not working on alias zones --- .changelog/e9b2bb64775a4b88a993bde159330b23.md | 4 ++++ octodns/manager.py | 8 +++++++- octodns/zone.py | 6 ++++++ 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 .changelog/e9b2bb64775a4b88a993bde159330b23.md diff --git a/.changelog/e9b2bb64775a4b88a993bde159330b23.md b/.changelog/e9b2bb64775a4b88a993bde159330b23.md new file mode 100644 index 0000000..b774278 --- /dev/null +++ b/.changelog/e9b2bb64775a4b88a993bde159330b23.md @@ -0,0 +1,4 @@ +--- +type: patch +--- +Fix Templating processor not working on alias zones \ No newline at end of file diff --git a/octodns/manager.py b/octodns/manager.py index a18b255..3b021d1 100644 --- a/octodns/manager.py +++ b/octodns/manager.py @@ -4,6 +4,7 @@ from collections import deque from concurrent.futures import ThreadPoolExecutor +from copy import deepcopy from hashlib import sha256 from importlib import import_module from importlib.metadata import PackageNotFoundError @@ -500,7 +501,7 @@ class Manager(object): if desired: # This is an alias zone, rather than populate it we'll copy the # records over from `desired`. - for _, records in desired._records.items(): + for _, records in desired._pre_processing_records.items(): for record in records: zone.add_record(record.copy(zone=zone), lenient=lenient) else: @@ -520,6 +521,10 @@ class Manager(object): ) source.populate(zone) + # Frozen copy of zone records *before* going through processors so + # alias zone can copy them if needed. + zone._pre_processing_records = deepcopy(zone._records) + for processor in processors: zone = processor.process_source_zone(zone, sources=sources) @@ -690,6 +695,7 @@ class Manager(object): raise ManagerException(msg) aliased_zones[zone_name] = source_zone + self.log.info('sync: alias_of=%s', source_zone) continue lenient = config.get('lenient', False) diff --git a/octodns/zone.py b/octodns/zone.py index a94cda9..2a6d26b 100644 --- a/octodns/zone.py +++ b/octodns/zone.py @@ -90,6 +90,12 @@ class Zone(object): # node that we always store things with Record.name which will be idna # encoded thus we don't have to deal with idna/utf8 collisions self._records = defaultdict(set) + # Frozen copy of populated records set just before calling processors in + # Manager._populate_and_plan(), so alias zones can be populated with + # "raw" records instead of already processed records. + # This is especially useful for processors who changes records based on + # zone/records data like Templating processor. + self._pre_processing_records = None self._root_ns = None # optional leading . to match empty hostname # optional trailing . b/c some sources don't have it on their fqdn From c4dc79b64c32a068e6aa4bf44564ec37e32ab94d Mon Sep 17 00:00:00 2001 From: Jonathan Leroy Date: Tue, 29 Jul 2025 18:06:11 +0200 Subject: [PATCH 3/6] Revert: Fix Templating processor not working on alias zones --- .changelog/7e0022f1507d44d69422e92ce4d0a1c1.md | 4 ++++ .changelog/e9b2bb64775a4b88a993bde159330b23.md | 4 ---- octodns/manager.py | 8 +------- octodns/zone.py | 6 ------ 4 files changed, 5 insertions(+), 17 deletions(-) create mode 100644 .changelog/7e0022f1507d44d69422e92ce4d0a1c1.md delete mode 100644 .changelog/e9b2bb64775a4b88a993bde159330b23.md diff --git a/.changelog/7e0022f1507d44d69422e92ce4d0a1c1.md b/.changelog/7e0022f1507d44d69422e92ce4d0a1c1.md new file mode 100644 index 0000000..3b0feab --- /dev/null +++ b/.changelog/7e0022f1507d44d69422e92ce4d0a1c1.md @@ -0,0 +1,4 @@ +--- +type: none +--- +Revert: Fix Templating processor not working on alias zones \ No newline at end of file diff --git a/.changelog/e9b2bb64775a4b88a993bde159330b23.md b/.changelog/e9b2bb64775a4b88a993bde159330b23.md deleted file mode 100644 index b774278..0000000 --- a/.changelog/e9b2bb64775a4b88a993bde159330b23.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -type: patch ---- -Fix Templating processor not working on alias zones \ No newline at end of file diff --git a/octodns/manager.py b/octodns/manager.py index 3b021d1..a18b255 100644 --- a/octodns/manager.py +++ b/octodns/manager.py @@ -4,7 +4,6 @@ from collections import deque from concurrent.futures import ThreadPoolExecutor -from copy import deepcopy from hashlib import sha256 from importlib import import_module from importlib.metadata import PackageNotFoundError @@ -501,7 +500,7 @@ class Manager(object): if desired: # This is an alias zone, rather than populate it we'll copy the # records over from `desired`. - for _, records in desired._pre_processing_records.items(): + for _, records in desired._records.items(): for record in records: zone.add_record(record.copy(zone=zone), lenient=lenient) else: @@ -521,10 +520,6 @@ class Manager(object): ) source.populate(zone) - # Frozen copy of zone records *before* going through processors so - # alias zone can copy them if needed. - zone._pre_processing_records = deepcopy(zone._records) - for processor in processors: zone = processor.process_source_zone(zone, sources=sources) @@ -695,7 +690,6 @@ class Manager(object): raise ManagerException(msg) aliased_zones[zone_name] = source_zone - self.log.info('sync: alias_of=%s', source_zone) continue lenient = config.get('lenient', False) diff --git a/octodns/zone.py b/octodns/zone.py index 2a6d26b..a94cda9 100644 --- a/octodns/zone.py +++ b/octodns/zone.py @@ -90,12 +90,6 @@ class Zone(object): # node that we always store things with Record.name which will be idna # encoded thus we don't have to deal with idna/utf8 collisions self._records = defaultdict(set) - # Frozen copy of populated records set just before calling processors in - # Manager._populate_and_plan(), so alias zones can be populated with - # "raw" records instead of already processed records. - # This is especially useful for processors who changes records based on - # zone/records data like Templating processor. - self._pre_processing_records = None self._root_ns = None # optional leading . to match empty hostname # optional trailing . b/c some sources don't have it on their fqdn From a5a1d504535985532e213751c73113efd21bd401 Mon Sep 17 00:00:00 2001 From: Jonathan Leroy Date: Tue, 29 Jul 2025 19:13:57 +0200 Subject: [PATCH 4/6] Add trailing_dots parameter to templating processor --- .../3e57e696039c4f37a3062043be81199c.md | 4 ++ octodns/processor/templating.py | 34 +++++++++---- tests/test_octodns_processor_templating.py | 48 ++++++++++++++++++- 3 files changed, 76 insertions(+), 10 deletions(-) create mode 100644 .changelog/3e57e696039c4f37a3062043be81199c.md diff --git a/.changelog/3e57e696039c4f37a3062043be81199c.md b/.changelog/3e57e696039c4f37a3062043be81199c.md new file mode 100644 index 0000000..8b9ac97 --- /dev/null +++ b/.changelog/3e57e696039c4f37a3062043be81199c.md @@ -0,0 +1,4 @@ +--- +type: patch +--- +Add trailing_dots parameter to templating processor \ No newline at end of file diff --git a/octodns/processor/templating.py b/octodns/processor/templating.py index 6477872..0f34454 100644 --- a/octodns/processor/templating.py +++ b/octodns/processor/templating.py @@ -15,6 +15,10 @@ class Templating(BaseProcessor): templating: class: octodns.processor.templating.Templating + # When `trailing_dots` is disabled, trailing dots are removed from all + # built-in variables values who represent a FQDN, like `{zone_name}` + # or `{record_fqdn}`. Optional. Default to `True`. + trailing_dots: False # Any k/v present in context will be passed into the .format method and # thus be available as additional variables in the template. This is all # optional. @@ -55,16 +59,17 @@ class Templating(BaseProcessor): ''' - def __init__(self, id, *args, context={}, **kwargs): + def __init__(self, id, *args, trailing_dots=True, context={}, **kwargs): super().__init__(id, *args, **kwargs) + self.trailing_dots = trailing_dots self.context = context def process_source_zone(self, desired, sources): sources = sources or [] zone_params = { - 'zone_name': desired.decoded_name.rstrip('.'), - 'zone_decoded_name': desired.decoded_name.rstrip('.'), - 'zone_encoded_name': desired.name.rstrip('.'), + '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 @@ -75,20 +80,33 @@ class Templating(BaseProcessor): for k, v in self.context.items() }, } + if not self.trailing_dots: + zone_params = zone_params | { + 'zone_name': desired.decoded_name[:-1], + 'zone_decoded_name': desired.decoded_name[:-1], + 'zone_encoded_name': desired.name[:-1], + } def params(record): - return { + record_params = { 'record_name': record.decoded_name, 'record_decoded_name': record.decoded_name, 'record_encoded_name': record.name, - 'record_fqdn': record.decoded_fqdn.rstrip('.'), - 'record_decoded_fqdn': record.decoded_fqdn.rstrip('.'), - 'record_encoded_fqdn': record.fqdn.rstrip('.'), + 'record_fqdn': record.decoded_fqdn, + 'record_decoded_fqdn': record.decoded_fqdn, + 'record_encoded_fqdn': record.fqdn, 'record_type': record._type, 'record_ttl': record.ttl, 'record_source_id': record.source.id if record.source else None, **zone_params, } + if not self.trailing_dots: + record_params = record_params | { + 'record_fqdn': record.decoded_fqdn[:-1], + 'record_decoded_fqdn': record.decoded_fqdn[:-1], + 'record_encoded_fqdn': record.fqdn[:-1], + } + return record_params for record in desired.records: if hasattr(record, 'values'): diff --git a/tests/test_octodns_processor_templating.py b/tests/test_octodns_processor_templating.py index 7c90706..a313286 100644 --- a/tests/test_octodns_processor_templating.py +++ b/tests/test_octodns_processor_templating.py @@ -74,7 +74,7 @@ class TemplatingTest(TestCase): { 'type': 'CNAME', 'ttl': 42, - 'value': '_cname.{zone_name}.something.else.', + 'value': '_cname.{zone_name}something.else.', }, lenient=True, ) @@ -107,7 +107,7 @@ class TemplatingTest(TestCase): { 'type': 'TXT', 'ttl': 42, - 'value': 'There are {zone_num_records} record(s) in {zone_name}.', + 'value': 'There are {zone_num_records} record(s) in {zone_name}', }, ) zone.add_record(txt) @@ -162,6 +162,50 @@ class TemplatingTest(TestCase): ) 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, + ) + + @patch('octodns.record.TxtValue.template') + def test_trailing_dots(self, mock_template): + templ = Templating('test', trailing_dots=False) + + 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')] ) From 871b2f2a7ee0e4e02ab35cd3f2ffec8be0245ce1 Mon Sep 17 00:00:00 2001 From: Ross McFarland Date: Tue, 29 Jul 2025 11:00:10 -0700 Subject: [PATCH 5/6] Improve error messaging for unknown templating parameters --- .../af8522cac7e54d22a615eab351d445b3.md | 4 ++ octodns/processor/templating.py | 26 +++++++++-- tests/test_octodns_processor_templating.py | 44 ++++++++++++++++++- 3 files changed, 70 insertions(+), 4 deletions(-) create mode 100644 .changelog/af8522cac7e54d22a615eab351d445b3.md diff --git a/.changelog/af8522cac7e54d22a615eab351d445b3.md b/.changelog/af8522cac7e54d22a615eab351d445b3.md new file mode 100644 index 0000000..72130ef --- /dev/null +++ b/.changelog/af8522cac7e54d22a615eab351d445b3.md @@ -0,0 +1,4 @@ +--- +type: patch +--- +Improve error messaging for unknown templating parameters \ No newline at end of file diff --git a/octodns/processor/templating.py b/octodns/processor/templating.py index c514bec..6436f48 100644 --- a/octodns/processor/templating.py +++ b/octodns/processor/templating.py @@ -5,6 +5,14 @@ from octodns.processor.base import BaseProcessor +class TemplatingError(Exception): + + def __init__(self, record, msg): + self.record = record + msg = f'Invalid record "{record.fqdn}", {msg}' + super().__init__(msg) + + class Templating(BaseProcessor): ''' Record templating using python format. For simple records like TXT and CAA @@ -76,7 +84,7 @@ class Templating(BaseProcessor): }, } - def params(record): + def build_params(record): return { 'record_name': record.decoded_name, 'record_decoded_name': record.decoded_name, @@ -90,12 +98,24 @@ class Templating(BaseProcessor): **zone_params, } + def template(value, params, record): + try: + return value.template(params) + except KeyError as e: + raise TemplatingError( + record, + f'undefined template parameter "{e.args[0]}" in value', + ) from e + for record in desired.records: + params = build_params(record) if hasattr(record, 'values'): if record.values and not hasattr(record.values[0], 'template'): # the (custom) value type does not support templating continue - new_values = [v.template(params(record)) for v in record.values] + new_values = [ + template(v, params, record) for v in record.values + ] if record.values != new_values: new = record.copy() new.values = new_values @@ -104,7 +124,7 @@ class Templating(BaseProcessor): if not hasattr(record.value, 'template'): # the (custom) value type does not support templating continue - new_value = record.value.template(params(record)) + new_value = template(record.value, params, record) if record.value != new_value: new = record.copy() new.value = new_value diff --git a/tests/test_octodns_processor_templating.py b/tests/test_octodns_processor_templating.py index 94472d8..761ec06 100644 --- a/tests/test_octodns_processor_templating.py +++ b/tests/test_octodns_processor_templating.py @@ -5,7 +5,7 @@ from unittest import TestCase from unittest.mock import call, patch -from octodns.processor.templating import Templating +from octodns.processor.templating import Templating, TemplatingError from octodns.record import Record, ValueMixin, ValuesMixin from octodns.zone import Zone @@ -225,3 +225,45 @@ class TemplatingTest(TestCase): self.assertEqual('num_sources: 3', txt.values[0]) self.assertEqual('the_answer: 42', txt.values[1]) self.assertEqual('the_date: today', txt.values[2]) + + def test_bad_key(self): + templ = Templating('test') + + zone = Zone('unit.tests.', []) + txt = Record.new( + zone, + 'txt', + {'type': 'TXT', 'ttl': 42, 'value': 'this {bad} does not exist'}, + ) + zone.add_record(txt) + + with self.assertRaises(TemplatingError) as ctx: + templ.process_source_zone( + zone, tuple(DummySource(i) for i in range(3)) + ) + self.assertEqual( + 'Invalid record "txt.unit.tests.", undefined template parameter "bad" in value', + str(ctx.exception), + ) + + zone = Zone('unit.tests.', []) + cname = Record.new( + zone, + 'cname', + { + 'type': 'CNAME', + 'ttl': 42, + 'value': '_cname.{bad}something.else.', + }, + lenient=True, + ) + zone.add_record(cname) + + with self.assertRaises(TemplatingError) as ctx: + templ.process_source_zone( + zone, tuple(DummySource(i) for i in range(3)) + ) + self.assertEqual( + 'Invalid record "cname.unit.tests.", undefined template parameter "bad" in value', + str(ctx.exception), + ) From fad8c039863c9f71d6ea8ee7cd55385dc6de4e4d Mon Sep 17 00:00:00 2001 From: Jonathan Leroy Date: Wed, 30 Jul 2025 14:14:04 +0200 Subject: [PATCH 6/6] Change in the way templating parameters are handled when trailing_dots is disabled --- .../3e57e696039c4f37a3062043be81199c.md | 4 +- .../6c6407b8ddf345ecb2e850e34d1ce328.md | 4 -- .../7e0022f1507d44d69422e92ce4d0a1c1.md | 4 -- octodns/processor/templating.py | 41 ++++++++++--------- tests/test_octodns_processor_templating.py | 2 +- 5 files changed, 24 insertions(+), 31 deletions(-) delete mode 100644 .changelog/6c6407b8ddf345ecb2e850e34d1ce328.md delete mode 100644 .changelog/7e0022f1507d44d69422e92ce4d0a1c1.md diff --git a/.changelog/3e57e696039c4f37a3062043be81199c.md b/.changelog/3e57e696039c4f37a3062043be81199c.md index 8b9ac97..56c3112 100644 --- a/.changelog/3e57e696039c4f37a3062043be81199c.md +++ b/.changelog/3e57e696039c4f37a3062043be81199c.md @@ -1,4 +1,4 @@ --- -type: patch +type: minor --- -Add trailing_dots parameter to templating processor \ No newline at end of file +Add trailing_dots parameter to templating processor diff --git a/.changelog/6c6407b8ddf345ecb2e850e34d1ce328.md b/.changelog/6c6407b8ddf345ecb2e850e34d1ce328.md deleted file mode 100644 index 4f36618..0000000 --- a/.changelog/6c6407b8ddf345ecb2e850e34d1ce328.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -type: patch ---- -Removes the final dot from zone_*name and record_*fqdn templating variables \ No newline at end of file diff --git a/.changelog/7e0022f1507d44d69422e92ce4d0a1c1.md b/.changelog/7e0022f1507d44d69422e92ce4d0a1c1.md deleted file mode 100644 index 3b0feab..0000000 --- a/.changelog/7e0022f1507d44d69422e92ce4d0a1c1.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -type: none ---- -Revert: Fix Templating processor not working on alias zones \ No newline at end of file diff --git a/octodns/processor/templating.py b/octodns/processor/templating.py index 0f34454..2f74bc6 100644 --- a/octodns/processor/templating.py +++ b/octodns/processor/templating.py @@ -66,10 +66,17 @@ class Templating(BaseProcessor): def process_source_zone(self, desired, sources): sources = sources or [] + zone_name = desired.decoded_name + zone_decoded_name = desired.decoded_name + zone_encoded_name = desired.name + if not self.trailing_dots: + zone_name = zone_name[:-1] + zone_decoded_name = zone_decoded_name[:-1] + zone_encoded_name = zone_encoded_name[:-1] zone_params = { - 'zone_name': desired.decoded_name, - 'zone_decoded_name': desired.decoded_name, - 'zone_encoded_name': desired.name, + 'zone_name': zone_name, + 'zone_decoded_name': zone_decoded_name, + 'zone_encoded_name': zone_encoded_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 @@ -80,33 +87,27 @@ class Templating(BaseProcessor): for k, v in self.context.items() }, } - if not self.trailing_dots: - zone_params = zone_params | { - 'zone_name': desired.decoded_name[:-1], - 'zone_decoded_name': desired.decoded_name[:-1], - 'zone_encoded_name': desired.name[:-1], - } def params(record): - record_params = { + record_fqdn = record.decoded_fqdn + record_decoded_fqdn = record.decoded_fqdn + record_encoded_fqdn = record.fqdn + if not self.trailing_dots: + record_fqdn = record_fqdn[:-1] + record_decoded_fqdn = record_decoded_fqdn[:-1] + record_encoded_fqdn = record_encoded_fqdn[:-1] + return { 'record_name': record.decoded_name, 'record_decoded_name': record.decoded_name, 'record_encoded_name': record.name, - 'record_fqdn': record.decoded_fqdn, - 'record_decoded_fqdn': record.decoded_fqdn, - 'record_encoded_fqdn': record.fqdn, + 'record_fqdn': record_fqdn, + 'record_decoded_fqdn': record_decoded_fqdn, + 'record_encoded_fqdn': record_encoded_fqdn, 'record_type': record._type, 'record_ttl': record.ttl, 'record_source_id': record.source.id if record.source else None, **zone_params, } - if not self.trailing_dots: - record_params = record_params | { - 'record_fqdn': record.decoded_fqdn[:-1], - 'record_decoded_fqdn': record.decoded_fqdn[:-1], - 'record_encoded_fqdn': record.fqdn[:-1], - } - return record_params for record in desired.records: if hasattr(record, 'values'): diff --git a/tests/test_octodns_processor_templating.py b/tests/test_octodns_processor_templating.py index a313286..3c58174 100644 --- a/tests/test_octodns_processor_templating.py +++ b/tests/test_octodns_processor_templating.py @@ -156,7 +156,7 @@ class TemplatingTest(TestCase): { 'type': 'TXT', 'ttl': 42, - 'value': 'There are {zone_num_records} record(s) in {zone_name}.', + 'value': 'There are {zone_num_records} record(s) in {zone_name}', }, source=record_source, )