From fac662a9ec09697e7c5a95724ab1255e7fff1a27 Mon Sep 17 00:00:00 2001 From: Tobias Mueller Date: Thu, 16 Oct 2025 22:03:09 -0700 Subject: [PATCH] processors: Added a simple TTL clamping processor This will come in handy for APIs not supporting TTL outside a certain range. The Spaceship API, for example, only allows TTLs in the range of 5..3600 but rewriting a whole zone for Spaceship only seems not as convenient as clamping the values as they flow through OctoDNS. The code is coming from Claude. My prompt was: Write a simple OctoDNS processor that clamps TTL values. I followed up with: AttributeError: 'TtlClampProcessor' object has no attribute 'log' Where should self.log come from? And finally: Hm. I suspect the code is not running, somehow. It doesn't seem to be getting control. I put a raise in the process_record method but it doesn't fail. And I don't see any clamped values nor log output. So it took three attempts to make it produce something useful. --- octodns/processor/clamp.py | 63 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 octodns/processor/clamp.py diff --git a/octodns/processor/clamp.py b/octodns/processor/clamp.py new file mode 100644 index 0000000..4bfaeb5 --- /dev/null +++ b/octodns/processor/clamp.py @@ -0,0 +1,63 @@ +from logging import getLogger +from octodns.processor.base import BaseProcessor + + +class TtlClampProcessor(BaseProcessor): + """ + Processor that clamps TTL values to a specified range. + + Configuration: + min_ttl: Minimum TTL value (default: 300 seconds / 5 minutes) + max_ttl: Maximum TTL value (default: 86400 seconds / 24 hours) + + Example config.yaml: + processors: + clamp: + class: octodns.processor.clamp.TtlClampProcessor + min_ttl: 300 + max_ttl: 3600 + + zones: + example.com.: + sources: + - config + processors: + - clamp + targets: + - route53 + """ + + def __init__(self, id, min_ttl=300, max_ttl=86400): + super().__init__(id) + self.log = getLogger(f'{self.__class__.__module__}.{self.__class__.__name__}') + self.min_ttl = min_ttl + self.max_ttl = max_ttl + self.log.info( + f'TtlClampProcessor initialized: min={min_ttl}s, max={max_ttl}s' + ) + + def process_source_zone(self, desired, sources): + """ + Process records from source zone(s). + + Args: + desired: Zone object containing the desired records + sources: List of source names + + Returns: + The modified zone + """ + self.log.debug(f'Processing source zone: {desired.name}') + + for record in desired.records: + original_ttl = record.ttl + clamped_ttl = max(self.min_ttl, min(self.max_ttl, original_ttl)) + + if clamped_ttl != original_ttl: + self.log.info( + f'Clamping TTL for {record.fqdn} ({record._type}): ' + f'{original_ttl}s -> {clamped_ttl}s' + ) + record.ttl = clamped_ttl + + return desired