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

Add DS record support #83

Merged
merged 1 commit into from
Feb 5, 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
### Important

* Add `append_to_names` tag append parameter to sources
* Add `DS` record type support

## v0.0.6 - 2023-10-16 - Long overdue

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ zones:

#### Records

A, AAAA, CAA, CNAME, MX, NAPTR, NS, PTR, SPF, SRV, TXT
A, AAAA, CAA, CNAME, DS, MX, NAPTR, NS, PTR, SPF, SRV, TXT

#### Root NS Records

Expand Down
28 changes: 28 additions & 0 deletions octodns_route53/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,9 +312,15 @@ def _values_for_quoted(self, record):
def _value_for_SRV(self, value, record):
return f'{value.priority} {value.weight} {value.port} {value.target}'

def _value_for_DS(self, value, record):
return f'{value.key_tag} {value.algorithm} {value.digest_type} {value.digest}'

def _values_for_SRV(self, record):
return [self._value_for_SRV(v, record) for v in record.values]

def _values_for_DS(self, record):
return [self._value_for_DS(v, record) for v in record.values]


class _Route53Alias(_Route53Record):
def __init__(self, provider, hosted_zone_id, record, value, creating):
Expand Down Expand Up @@ -752,6 +758,7 @@ class Route53Provider(_AuthMixin, BaseProvider):
'AAAA',
'CAA',
'CNAME',
'DS',
'MX',
'NAPTR',
'NS',
Expand Down Expand Up @@ -1000,6 +1007,27 @@ def _data_for_SRV(self, rrset):
'ttl': int(rrset['TTL']),
}

def _data_for_DS(self, rrset):
values = []
for rr in rrset['ResourceRecords']:
# digest may contain whitespace
key_tag, algorithm, digest_type, digest = rr['Value'].split(
maxsplit=3
)
values.append(
{
'key_tag': key_tag,
'algorithm': algorithm,
'digest_type': digest_type,
'digest': digest,
}
)
return {
'type': rrset['Type'],
'values': values,
'ttl': int(rrset['TTL']),
}

def _load_records(self, zone_id):
if zone_id not in self._r53_rrsets:
self.log.debug('_load_records: zone_id=%s loading', zone_id)
Expand Down
49 changes: 37 additions & 12 deletions tests/test_octodns_provider_route53.py
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,21 @@ class TestRoute53Provider(TestCase):
'value': {'flags': 0, 'tag': 'issue', 'value': 'ca.unit.tests'},
},
),
(
'dskey',
{
'ttl': 70,
'type': 'DS',
'values': [
{
'key_tag': 60485,
'algorithm': 5,
'digest_type': 1,
'digest': '2BB183AF5F22588179A53B0A 98631FAD1A292118',
}
],
},
),
(
'alias',
{
Expand Down Expand Up @@ -1114,6 +1129,16 @@ def test_populate(self):
'ResourceRecords': [{'Value': '0 issue "ca.unit.tests"'}],
'TTL': 69,
},
{
'Name': 'dskey.unit.tests.',
'Type': 'DS',
'ResourceRecords': [
{
'Value': '60485 5 1 2BB183AF5F22588179A53B0A 98631FAD1A292118'
}
],
'TTL': 70,
},
{
'AliasTarget': {
'HostedZoneId': 'Z119WBBTVP5WFX',
Expand Down Expand Up @@ -1243,7 +1268,7 @@ def test_sync(self):
)

plan = provider.plan(self.expected)
self.assertEqual(12, len(plan.changes))
self.assertEqual(13, len(plan.changes))
self.assertTrue(plan.exists)
for change in plan.changes:
self.assertIsInstance(change, Create)
Expand All @@ -1270,7 +1295,7 @@ def test_sync(self):
{'HostedZoneId': 'z42', 'ChangeBatch': ANY},
)

self.assertEqual(12, provider.apply(plan))
self.assertEqual(13, provider.apply(plan))
stubber.assert_no_pending_responses()

# Delete by monkey patching in a populate that includes an extra record
Expand Down Expand Up @@ -1536,7 +1561,7 @@ def test_sync_create(self):
stubber.add_response('list_hosted_zones', list_hosted_zones_resp, {})

plan = provider.plan(self.expected)
self.assertEqual(12, len(plan.changes))
self.assertEqual(13, len(plan.changes))
self.assertFalse(plan.exists)
for change in plan.changes:
self.assertIsInstance(change, Create)
Expand Down Expand Up @@ -1608,7 +1633,7 @@ def test_sync_create(self):
{'HostedZoneId': 'z42', 'ChangeBatch': ANY},
)

self.assertEqual(12, provider.apply(plan))
self.assertEqual(13, provider.apply(plan))
stubber.assert_no_pending_responses()

def test_sync_create_with_delegation_set(self):
Expand All @@ -1625,7 +1650,7 @@ def test_sync_create_with_delegation_set(self):
stubber.add_response('list_hosted_zones', list_hosted_zones_resp, {})

plan = provider.plan(self.expected)
self.assertEqual(12, len(plan.changes))
self.assertEqual(13, len(plan.changes))
self.assertFalse(plan.exists)
for change in plan.changes:
self.assertIsInstance(change, Create)
Expand Down Expand Up @@ -1701,7 +1726,7 @@ def test_sync_create_with_delegation_set(self):
{'HostedZoneId': 'z42', 'ChangeBatch': ANY},
)

self.assertEqual(12, provider.apply(plan))
self.assertEqual(13, provider.apply(plan))
stubber.assert_no_pending_responses()

def test_health_checks_pagination(self):
Expand Down Expand Up @@ -2641,7 +2666,7 @@ def test_plan_apply_with_get_zones_by_name_zone_not_exists(self):
)

plan = provider.plan(self.expected)
self.assertEqual(12, len(plan.changes))
self.assertEqual(13, len(plan.changes))

create_hosted_zone_resp = {
'HostedZone': {
Expand Down Expand Up @@ -2709,7 +2734,7 @@ def test_plan_apply_with_get_zones_by_name_zone_not_exists(self):
{'HostedZoneId': 'z42', 'ChangeBatch': ANY},
)

self.assertEqual(12, provider.apply(plan))
self.assertEqual(13, provider.apply(plan))
stubber.assert_no_pending_responses()

def test_plan_apply_with_get_zones_by_name_zone_exists(self):
Expand Down Expand Up @@ -2760,7 +2785,7 @@ def test_plan_apply_with_get_zones_by_name_zone_exists(self):
)

plan = provider.plan(self.expected)
self.assertEqual(13, len(plan.changes))
self.assertEqual(14, len(plan.changes))

stubber.add_response(
'list_health_checks',
Expand All @@ -2784,7 +2809,7 @@ def test_plan_apply_with_get_zones_by_name_zone_exists(self):
{'HostedZoneId': 'z42', 'ChangeBatch': ANY},
)

self.assertEqual(13, provider.apply(plan))
self.assertEqual(14, provider.apply(plan))
stubber.assert_no_pending_responses()

def test_extra_change_no_health_check(self):
Expand Down Expand Up @@ -3450,7 +3475,7 @@ def _get_test_plan(self, max_changes):
@patch('octodns_route53.Route53Provider._really_apply')
def test_apply_1(self, really_apply_mock, _):
# 18 RRs with max of 19 should only get applied in one call
provider, plan = self._get_test_plan(19)
provider, plan = self._get_test_plan(20)
provider.apply(plan)
really_apply_mock.assert_called_once()

Expand All @@ -3468,7 +3493,7 @@ def test_apply_3(self, really_apply_mock, _):
# with a max of seven modifications, three calls
provider, plan = self._get_test_plan(7)
provider.apply(plan)
self.assertEqual(3, really_apply_mock.call_count)
self.assertEqual(4, really_apply_mock.call_count)

@patch('octodns_route53.Route53Provider._load_records')
@patch('octodns_route53.Route53Provider._really_apply')
Expand Down
Loading