diff --git a/octodns/record/base.py b/octodns/record/base.py index b6e3ed3..23b9a5c 100644 --- a/octodns/record/base.py +++ b/octodns/record/base.py @@ -123,6 +123,10 @@ class Record(EqualityTupleMixin): return records + @classmethod + def parse_rdata_texts(cls, rdatas): + return [cls._value_type.parse_rdata_text(r) for r in rdatas] + def __init__(self, zone, name, data, source=None): self.zone = zone if name: diff --git a/octodns/source/tinydns.py b/octodns/source/tinydns.py index 55a5eaf..384e9e5 100755 --- a/octodns/source/tinydns.py +++ b/octodns/source/tinydns.py @@ -357,6 +357,44 @@ class TinyDnsBaseSource(BaseSource): yield 'SRV', name, ttl, values + def _records_for_colon(self, zone, name, lines, arpa=False): + # :fqdn:n:rdata:ttl:timestamp:lo + # ANY + + if arpa: + # no arpa + return [] + + if not zone.owns('SRV', name): + # if name doesn't live under our zone there's nothing for us to do + return + + # group by lines by the record type + types = defaultdict(list) + for line in lines: + types[line[1].upper()].append(line) + + classes = Record.registered_types() + for _type, lines in types.items(): + _class = classes.get(_type, None) + if not _class: + self.log.info( + '_records_for_colon: unrecognized type %s, %s', _type, line + ) + continue + + # see if we can find a ttl on any of the lines, first one wins + ttl = self.default_ttl + for line in lines: + try: + ttl = int(line[3]) + break + except IndexError: + pass + + rdatas = [l[2] for l in lines] + yield _type, name, ttl, _class.parse_rdata_texts(rdatas) + def _records_for_six(self, zone, name, lines, arpa=False): # 6fqdn:ip:ttl:timestamp:lo # AAAA (arpa False) & PTR (arpa True) @@ -376,11 +414,8 @@ class TinyDnsBaseSource(BaseSource): '\'': _records_for_quote, # TXT '3': _records_for_three, # AAAA 'S': _records_for_S, # SRV + ':': _records_for_colon, # arbitrary '6': _records_for_six, # AAAA - # TODO: - # Sfqdn:ip:x:port:priority:weight:ttl:timestamp:lo - #':': _record_for_semicolon # arbitrary - # :fqdn:n:rdata:ttl:timestamp:lo } def _process_lines(self, zone, lines): diff --git a/tests/test_octodns_record.py b/tests/test_octodns_record.py index 88a7dfc..becc802 100644 --- a/tests/test_octodns_record.py +++ b/tests/test_octodns_record.py @@ -8,6 +8,7 @@ from octodns.idna import idna_encode from octodns.record import ( AliasRecord, ARecord, + CnameRecord, Create, Delete, MxValue, @@ -176,6 +177,20 @@ class TestRecord(TestCase): # make sure there's nothing extra self.assertEqual(5, len(records)) + def test_parse_rdata_texts(self): + self.assertEqual(['2.3.4.5'], ARecord.parse_rdata_texts(['2.3.4.5'])) + self.assertEqual( + ['2.3.4.6', '3.4.5.7'], + ARecord.parse_rdata_texts(['2.3.4.6', '3.4.5.7']), + ) + self.assertEqual( + ['some.target.'], CnameRecord.parse_rdata_texts(['some.target.']) + ) + self.assertEqual( + ['some.target.', 'other.target.'], + CnameRecord.parse_rdata_texts(['some.target.', 'other.target.']), + ) + def test_values_mixin_data(self): # no values, no value or values in data a = ARecord(self.zone, '', {'type': 'A', 'ttl': 600, 'values': []}) diff --git a/tests/test_octodns_source_tinydns.py b/tests/test_octodns_source_tinydns.py index 81129b0..338c4d6 100644 --- a/tests/test_octodns_source_tinydns.py +++ b/tests/test_octodns_source_tinydns.py @@ -17,7 +17,7 @@ class TestTinyDnsFileSource(TestCase): def test_populate_normal(self): got = Zone('example.com.', []) self.source.populate(got) - self.assertEqual(28, len(got.records)) + self.assertEqual(30, len(got.records)) expected = Zone('example.com.', []) for name, data in ( @@ -177,6 +177,26 @@ class TestTinyDnsFileSource(TestCase): ], }, ), + ( + 'arbitrary-sshfp', + { + 'type': 'SSHFP', + 'ttl': 45, + 'values': [ + { + 'algorithm': 1, + 'fingerprint_type': 2, + 'fingerprint': '00479b27', + }, + { + 'algorithm': 2, + 'fingerprint_type': 2, + 'fingerprint': '00479a28', + }, + ], + }, + ), + ('arbitrary-a', {'type': 'A', 'ttl': 3600, 'value': '80.81.82.83'}), ): record = Record.new(expected, name, data) expected.add_record(record) @@ -253,4 +273,4 @@ class TestTinyDnsFileSource(TestCase): got = Zone('example.com.', ['sub']) self.source.populate(got) # we don't see one www.sub.example.com. record b/c it's in a sub - self.assertEqual(27, len(got.records)) + self.assertEqual(29, len(got.records)) diff --git a/tests/zones/tinydns/example.com b/tests/zones/tinydns/example.com index 301269f..3369a28 100755 --- a/tests/zones/tinydns/example.com +++ b/tests/zones/tinydns/example.com @@ -72,3 +72,11 @@ S_a._tcp.example.com::target.somewhere.else:8080:10:50:43 S_b._tcp.example.com:56.57.58.59:target.srv.example.com.:9999 # complete duplicate should be ignored S_b._tcp.example.com:56.57.58.59:target.srv.example.com.:9999 + +# arbitrary multi-value non-spec record +:arbitrary-sshfp.example.com:SSHFP:2 2 00479a28 +:arbitrary-sshfp.example.com:SSHFP:1 2 00479b27:45 +# does not make sense to do an A this way, but it'll work +:arbitrary-a.example.com:a:80.81.82.83 +# this should just be inored b/c the type is unknown +:arbitrary-invalid.example.com:invalid:does not matter:99