From af26747f87196559423258d62cf81cb787782a6a Mon Sep 17 00:00:00 2001 From: Michal Wojcik Date: Mon, 9 Feb 2026 15:34:32 +0100 Subject: [PATCH 1/3] domain_record TTLs are not rounded to closest valid values when diffing --- plugins/modules/domain_record.py | 41 ++++++++-- .../targets/domain_record/tasks/main.yaml | 75 +++++++++++++++++++ 2 files changed, 110 insertions(+), 6 deletions(-) diff --git a/plugins/modules/domain_record.py b/plugins/modules/domain_record.py index f6d5888b..dde3b03d 100644 --- a/plugins/modules/domain_record.py +++ b/plugins/modules/domain_record.py @@ -110,9 +110,11 @@ type=FieldType.integer, editable=True, description=[ - "The amount of time in seconds that this Domain’s " - "records may be cached by resolvers " - "or other domain servers." + "Time to Live - the amount of time in seconds that this Domain's " + "records may be cached by resolvers or other domain servers. " + "Valid values are 300, 3600, 7200, 14400, 28800, 57600, 86400, " + "172800, 345600, 604800, 1209600, and 2419200 - any other value " + "will be rounded to the nearest valid value." ], ), "type": SpecField( @@ -158,8 +160,11 @@ "weight", } -DOCUMENTATION = r""" -""" +# Valid TTL values for domain records +VALID_TTL_VALUES = [ + 300, 3600, 7200, 14400, 28800, 57600, 86400, + 172800, 345600, 604800, 1209600, 2419200 +] EXAMPLES = r""" """ RETURN = r""" @@ -187,6 +192,21 @@ def __init__(self) -> None: mutually_exclusive=self.mutually_exclusive, ) + def _normalize_ttl_sec(self, ttl_value: Optional[int]) -> Optional[int]: + """Normalize TTL value to the nearest valid value.""" + if ttl_value is None: + return None + + # Find the closest valid TTL value + closest = min(VALID_TTL_VALUES, key=lambda x: abs(x - ttl_value)) + + if closest != ttl_value: + self.warn( + f"TTL value {ttl_value} is not valid. Using nearest valid value: {closest}" + ) + + return closest + def _find_record( self, domain: Domain, name: str, rtype: str, target: str ) -> Optional[DomainRecord]: @@ -268,6 +288,10 @@ def _create_record(self) -> Optional[DomainRecord]: record_name = params.get("name") record_service = params.get("service") + # Normalize TTL value if provided + if "ttl_sec" in params: + params["ttl_sec"] = self._normalize_ttl_sec(params["ttl_sec"]) + try: self.register_action( "Created domain record type {0}: name is {1}; service is {2}".format( @@ -283,9 +307,14 @@ def _create_record(self) -> Optional[DomainRecord]: def _update_record(self) -> None: """Handles all update functionality for the current Domain record""" + # Get filtered parameters and normalize TTL if present + params = filter_null_values(self.module.params) + if "ttl_sec" in params: + params["ttl_sec"] = self._normalize_ttl_sec(params["ttl_sec"]) + handle_updates( self._record, - filter_null_values(self.module.params), + params, MUTABLE_FIELDS, self.register_action, ignore_keys={"name", "record", "target"}, diff --git a/tests/integration/targets/domain_record/tasks/main.yaml b/tests/integration/targets/domain_record/tasks/main.yaml index 1e391598..4eb053b9 100644 --- a/tests/integration/targets/domain_record/tasks/main.yaml +++ b/tests/integration/targets/domain_record/tasks/main.yaml @@ -160,6 +160,81 @@ - record_dupe_update.record.ttl_sec == 300 - record_dupe_update.record.weight == 55 + - name: Create domain_record with invalid TTL (should round to nearest valid value) + linode.cloud.domain_record: + domain: '{{ domain_create.domain.domain }}' + name: 'ttl-test' + type: 'A' + target: '127.0.0.3' + ttl_sec: 4500 # Invalid value, should round to 3600 + state: present + register: record_ttl_invalid + + - name: Assert domain_record with invalid TTL is created with rounded value + assert: + that: + - record_ttl_invalid.record.name == 'ttl-test' + - record_ttl_invalid.record.type == 'A' + - record_ttl_invalid.record.target == '127.0.0.3' + - record_ttl_invalid.record.ttl_sec == 3600 # Rounded from 4500 to 3600 + + - name: Attempt to update with same invalid TTL (should be idempotent, no change) + linode.cloud.domain_record: + domain: '{{ domain_create.domain.domain }}' + record_id: '{{ record_ttl_invalid.record.id }}' + ttl_sec: 4500 # Same invalid value, should normalize to 3600 (no change) + state: present + register: record_ttl_idempotent + + - name: Assert no change occurred (idempotency check) + assert: + that: + - record_ttl_idempotent.changed == false + - record_ttl_idempotent.record.ttl_sec == 3600 + + - name: Test TTL rounding edge cases - value below minimum + linode.cloud.domain_record: + domain: '{{ domain_create.domain.domain }}' + name: 'ttl-min' + type: 'A' + target: '127.0.0.5' + ttl_sec: 100 # Below minimum 300, should round to 300 + state: present + register: record_ttl_min + + - name: Assert TTL below minimum rounds to 300 + assert: + that: + - record_ttl_min.record.name == 'ttl-min' + - record_ttl_min.record.ttl_sec == 300 # Rounded from 100 to 300 + + - name: Test TTL rounding edge cases - value above maximum + linode.cloud.domain_record: + domain: '{{ domain_create.domain.domain }}' + name: 'ttl-max' + type: 'A' + target: '127.0.0.6' + ttl_sec: 3000000 # Above maximum 2419200, should round to 2419200 + state: present + register: record_ttl_max + + - name: Assert TTL above maximum rounds to 2419200 + assert: + that: + - record_ttl_max.record.name == 'ttl-max' + - record_ttl_max.record.ttl_sec == 2419200 # Rounded from 3000000 to 2419200 + + - name: Clean up TTL test records + linode.cloud.domain_record: + domain: '{{ domain_create.domain.domain }}' + record_id: '{{ item }}' + state: absent + loop: + - '{{ record_ttl_invalid.record.id }}' + - '{{ record_ttl_min.record.id }}' + - '{{ record_ttl_max.record.id }}' + register: ttl_cleanup + - name: Create an SRV domain record linode.cloud.domain_record: domain: '{{ domain_create.domain.domain }}' From 1a08b7e6de4c459d8e44c6d286e6ea0f7d0dc533 Mon Sep 17 00:00:00 2001 From: Michal Wojcik Date: Mon, 9 Feb 2026 16:04:01 +0100 Subject: [PATCH 2/3] domain_record TTLs are not rounded to closest valid values when diffing --- plugins/modules/domain_record.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/modules/domain_record.py b/plugins/modules/domain_record.py index dde3b03d..7a4b71d3 100644 --- a/plugins/modules/domain_record.py +++ b/plugins/modules/domain_record.py @@ -165,6 +165,8 @@ 300, 3600, 7200, 14400, 28800, 57600, 86400, 172800, 345600, 604800, 1209600, 2419200 ] +DOCUMENTATION = r""" +""" EXAMPLES = r""" """ RETURN = r""" From 17ee5f2a1545d1d2538a4b8071f1816c273246bc Mon Sep 17 00:00:00 2001 From: Michal Wojcik Date: Tue, 10 Feb 2026 12:21:30 +0100 Subject: [PATCH 3/3] domain_record TTLs are not rounded to closest valid values when diffing --- docs/modules/domain_record.md | 2 +- plugins/modules/domain_record.py | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/docs/modules/domain_record.md b/docs/modules/domain_record.md index 770f92a5..c5c0b044 100644 --- a/docs/modules/domain_record.md +++ b/docs/modules/domain_record.md @@ -74,7 +74,7 @@ NOTE: Domain records are identified by their name, target, and type. | `service` |
`str`
|
Optional
| An underscore (_) is prepended and a period (.) is appended automatically to the submitted value for this property. Only valid and required for SRV record requests. The name of the service. **(Updatable)** | | `tag` |
`str`
|
Optional
| The tag portion of a CAA record. Only valid and required for CAA record requests. **(Updatable)** | | `target` |
`str`
|
Optional
| The target for this Record. | -| `ttl_sec` |
`int`
|
Optional
| The amount of time in seconds that this Domain’s records may be cached by resolvers or other domain servers. **(Updatable)** | +| `ttl_sec` |
`int`
|
Optional
| Time to Live - the amount of time in seconds that this Domain's records may be cached by resolvers or other domain servers. Valid values are 300, 3600, 7200, 14400, 28800, 57600, 86400, 172800, 345600, 604800, 1209600, and 2419200 - any other value will be rounded to the nearest valid value. **(Updatable)** | | `type` |
`str`
|
Optional
| The type of Record this is in the DNS system. | | `weight` |
`int`
|
Optional
| The relative weight of this Record used in the case of identical priority. **(Updatable)** | diff --git a/plugins/modules/domain_record.py b/plugins/modules/domain_record.py index 7a4b71d3..33fe5de6 100644 --- a/plugins/modules/domain_record.py +++ b/plugins/modules/domain_record.py @@ -162,8 +162,18 @@ # Valid TTL values for domain records VALID_TTL_VALUES = [ - 300, 3600, 7200, 14400, 28800, 57600, 86400, - 172800, 345600, 604800, 1209600, 2419200 + 300, + 3600, + 7200, + 14400, + 28800, + 57600, + 86400, + 172800, + 345600, + 604800, + 1209600, + 2419200, ] DOCUMENTATION = r""" """