Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added escaping semicolon for TXT records #44

Merged
merged 2 commits into from
Mar 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## v0.99.2 - 2024-03-14 - Add escaping semicolon for TXT records
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Standard practice w/octodns modules is that the version bumps and changelog header update happen in a dedicated release PR. we can leave this in here for now since it doesn't look like the actual __version__ bump happened.


#### Changes

* Add escaping semicolon for TXT records in `SelectelProvider` and `SelectelProviderLegacy`


## v0.99.1 - 2024-02-01 - Fix project structure for distribution

#### Changes
Expand Down
6 changes: 6 additions & 0 deletions octodns_selectel/escaping_semicolon.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
def escape_semicolon(s):
return s.replace(';', '\\;')


def unescape_semicolon(s):
return s.replace('\\;', ';')
16 changes: 14 additions & 2 deletions octodns_selectel/v1/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
from octodns.provider.base import BaseProvider
from octodns.record import Record, Update

from octodns_selectel.escaping_semicolon import (
escape_semicolon,
unescape_semicolon,
)
from octodns_selectel.version import __version__ as provider_version


Expand Down Expand Up @@ -149,6 +153,15 @@ def _params_for_single(self, record):
'type': record._type,
}

def _params_for_TXT(self, record):
for value in record.values:
yield {
'content': unescape_semicolon(value),
'name': record.fqdn,
'ttl': max(self.MIN_TTL, record.ttl),
'type': record._type,
}

def _params_for_MX(self, record):
for value in record.values:
yield {
Expand Down Expand Up @@ -185,7 +198,6 @@ def _params_for_SSHFP(self, record):
_params_for_A = _params_for_multiple
_params_for_AAAA = _params_for_multiple
_params_for_NS = _params_for_multiple
_params_for_TXT = _params_for_multiple

_params_for_CNAME = _params_for_single
_params_for_ALIAS = _params_for_single
Expand Down Expand Up @@ -231,7 +243,7 @@ def _data_for_TXT(self, _type, records):
return {
'ttl': records[0]['ttl'],
'type': _type,
'values': [r['content'] for r in records],
'values': [escape_semicolon(r['content']) for r in records],
}

def _data_for_SRV(self, _type, records):
Expand Down
13 changes: 11 additions & 2 deletions octodns_selectel/v2/mappings.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
from string import Template

from octodns_selectel.escaping_semicolon import (
escape_semicolon,
unescape_semicolon,
)

from .exceptions import SelectelException


Expand All @@ -17,7 +22,8 @@ def to_selectel_rrset(record):
rrset_records = [{'content': record.value}]
elif record._type == "TXT":
rrset_records = [
dict(content=f'\"{value}\"') for value in record.values
dict(content=f'\"{unescape_semicolon(value)}\"')
for value in record.values
]
elif record._type == "MX":
rrset_records = list(
Expand Down Expand Up @@ -76,7 +82,10 @@ def to_octodns_record_data(rrset):
key_for_record_values = "value"
record_values = rrset["records"][0]["content"]
elif rrset_type == "TXT":
record_values = [r['content'].strip('"\'') for r in rrset["records"]]
record_values = [
escape_semicolon(r['content']).strip('"\'')
for r in rrset["records"]
]
elif rrset_type == "MX":
for record in rrset["records"]:
preference, exchange = record["content"].split(" ")
Expand Down
34 changes: 28 additions & 6 deletions tests/v1/test_provider_octodns_selectel_v1.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,28 @@ class TestSelectelProvider(TestCase):
)
)

# TXT with semicolon
api_record.append(
{
'type': 'TXT',
'ttl': 300,
'content': 'v=DKIM1; k=rsa; p=some-key',
'name': 'dkim.unit.tests',
'id': 18,
}
)
expected.add(
Record.new(
zone,
'dkim',
{
'ttl': 200,
'type': 'TXT',
'value': 'v=DKIM1\\; k=rsa\\; p=some-key',
},
)
)

# SSHFP
api_record.append(
{
Expand Down Expand Up @@ -378,8 +400,8 @@ def test_apply(self, fake_http):
zone.add_record(record)

plan = provider.plan(zone)
self.assertEqual(10, len(plan.changes))
self.assertEqual(10, provider.apply(plan))
self.assertEqual(11, len(plan.changes))
self.assertEqual(11, provider.apply(plan))

@requests_mock.Mocker()
def test_domain_list(self, fake_http):
Expand Down Expand Up @@ -450,8 +472,8 @@ def test_not_exist_domain(self, fake_http):
zone.add_record(record)

plan = provider.plan(zone)
self.assertEqual(10, len(plan.changes))
self.assertEqual(10, provider.apply(plan))
self.assertEqual(11, len(plan.changes))
self.assertEqual(11, provider.apply(plan))

@requests_mock.Mocker()
def test_delete_no_exist_record(self, fake_http):
Expand Down Expand Up @@ -516,8 +538,8 @@ def test_change_record(self, fake_http):
zone.add_record(record)

plan = provider.plan(zone)
self.assertEqual(10, len(plan.changes))
self.assertEqual(10, provider.apply(plan))
self.assertEqual(11, len(plan.changes))
self.assertEqual(11, provider.apply(plan))

@requests_mock.Mocker()
def test_include_change_returns_false(self, fake_http):
Expand Down
15 changes: 15 additions & 0 deletions tests/v2/test_selectel_mappings.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,8 @@ def test_mapping_record_aaaa(self):

def test_mapping_record_txt(self):
txt_list = ["\"Buzz\"", "\"Fizz\""]
unescaping_dkim_value = "\"v=DKIM1; k=rsa; p=some-key\""
escaping_dkim_value = "\"v=DKIM1\\; k=rsa\\; p=some-key\""
test_pairs = (
PairTest(
TxtRecord(
Expand Down Expand Up @@ -307,6 +309,19 @@ def test_mapping_record_txt(self):
],
),
),
PairTest(
TxtRecord(
self.zone,
"dkim",
dict(ttl=self.ttl, value=escaping_dkim_value),
),
dict(
name=f"dkim.{self.zone.name}",
ttl=self.ttl,
type="TXT",
records=[dict(content=unescaping_dkim_value)],
),
),
)
self._assert_mapping_common(test_pairs)
self._assert_mapping_values(test_pairs)
Expand Down
Loading