Browse Source

Merge branch 'main' into process-source-and-target-zones

pull/1114/head
Ross McFarland 2 years ago
committed by GitHub
parent
commit
0f582de47b
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 59 additions and 22 deletions
  1. +1
    -0
      CHANGELOG.md
  2. +2
    -2
      README.md
  3. +12
    -10
      octodns/record/base.py
  4. +44
    -10
      tests/test_octodns_record.py

+ 1
- 0
CHANGELOG.md View File

@ -1,6 +1,7 @@
## v1.?.0 - 2023-??-?? - ## v1.?.0 - 2023-??-?? -
* Record.lenient property added similar to other common/standard _octodns data * Record.lenient property added similar to other common/standard _octodns data
* Fix bug with Record.copy when values is an empty list []
* Processor.process_source_and_target_zones added to support modifying both the * Processor.process_source_and_target_zones added to support modifying both the
desired and/or existing zones just prior to computing changes. desired and/or existing zones just prior to computing changes.


+ 2
- 2
README.md View File

@ -313,8 +313,8 @@ Similar to providers, but can only serve to populate records into a zone, cannot
| Source | Record Support | Dynamic | Notes | | Source | Record Support | Dynamic | Notes |
|--|--|--|--| |--|--|--|--|
| [EnvVarSource](/octodns/source/envvar.py) | TXT | No | read-only environment variable injection | | [EnvVarSource](/octodns/source/envvar.py) | TXT | No | read-only environment variable injection |
| [AxfrSource](/octodns/source/axfr.py) | A, AAAA, CAA, CNAME, LOC, MX, NS, PTR, SPF, SRV, TXT | No | read-only |
| [ZoneFileSource](/octodns/source/axfr.py) | A, AAAA, CAA, CNAME, MX, NS, PTR, SPF, SRV, TXT | No | read-only |
| [AxfrSource](https://github.com/octodns/octodns-bind/) | A, AAAA, CAA, CNAME, LOC, MX, NS, PTR, SPF, SRV, TXT | No | read-only |
| [ZoneFileSource](https://github.com/octodns/octodns-bind/) | A, AAAA, CAA, CNAME, MX, NS, PTR, SPF, SRV, TXT | No | read-only |
| [TinyDnsFileSource](/octodns/source/tinydns.py) | A, CNAME, MX, NS, PTR | No | read-only | | [TinyDnsFileSource](/octodns/source/tinydns.py) | A, CNAME, MX, NS, PTR | No | read-only |
### Notes ### Notes


+ 12
- 10
octodns/record/base.py View File

@ -296,7 +296,10 @@ class ValuesMixin(object):
try: try:
values = data['values'] values = data['values']
except KeyError: except KeyError:
values = [data['value']]
try:
values = [data['value']]
except KeyError:
values = []
self.values = sorted(self._value_type.process(values)) self.values = sorted(self._value_type.process(values))
def changes(self, other, target): def changes(self, other, target):
@ -306,16 +309,16 @@ class ValuesMixin(object):
def _data(self): def _data(self):
ret = super()._data() ret = super()._data()
if len(self.values) > 1:
values = [getattr(v, 'data', v) for v in self.values if v]
if len(values) > 1:
ret['values'] = values
elif len(values) == 1:
ret['value'] = values[0]
elif len(self.values) == 1:
if len(self.values) == 1:
v = self.values[0] v = self.values[0]
if v: if v:
ret['value'] = getattr(v, 'data', v) ret['value'] = getattr(v, 'data', v)
else:
values = [getattr(v, 'data', v) for v in self.values if v]
if len(values) == 1:
ret['value'] = values[0]
else:
ret['values'] = values
return ret return ret
@ -371,8 +374,7 @@ class ValueMixin(object):
def _data(self): def _data(self):
ret = super()._data() ret = super()._data()
if self.value:
ret['value'] = getattr(self.value, 'data', self.value)
ret['value'] = getattr(self.value, 'data', self.value)
return ret return ret
@property @property


+ 44
- 10
tests/test_octodns_record.py View File

@ -197,19 +197,19 @@ class TestRecord(TestCase):
) )
def test_values_mixin_data(self): def test_values_mixin_data(self):
# no values, no value or values in data
# empty values -> empty values in data
a = ARecord(self.zone, '', {'type': 'A', 'ttl': 600, 'values': []}) a = ARecord(self.zone, '', {'type': 'A', 'ttl': 600, 'values': []})
self.assertNotIn('values', a.data)
self.assertEqual([], a.data['values'])
# empty value, no value or values in data # empty value, no value or values in data
b = ARecord(self.zone, '', {'type': 'A', 'ttl': 600, 'values': ['']}) b = ARecord(self.zone, '', {'type': 'A', 'ttl': 600, 'values': ['']})
self.assertNotIn('value', b.data) self.assertNotIn('value', b.data)
# empty/None values, no value or values in data
# empty/None values -> empty values in data
c = ARecord( c = ARecord(
self.zone, '', {'type': 'A', 'ttl': 600, 'values': ['', None]} self.zone, '', {'type': 'A', 'ttl': 600, 'values': ['', None]}
) )
self.assertNotIn('values', c.data)
self.assertEqual([], a.data['values'])
# empty/None values and valid, value in data # empty/None values and valid, value in data
c = ARecord( c = ARecord(
@ -225,13 +225,13 @@ class TestRecord(TestCase):
a = AliasRecord( a = AliasRecord(
self.zone, '', {'type': 'ALIAS', 'ttl': 600, 'value': None} self.zone, '', {'type': 'ALIAS', 'ttl': 600, 'value': None}
) )
self.assertNotIn('value', a.data)
self.assertIsNone(a.data['value'])
# unspecified value, no value in data # unspecified value, no value in data
a = AliasRecord( a = AliasRecord(
self.zone, '', {'type': 'ALIAS', 'ttl': 600, 'value': ''} self.zone, '', {'type': 'ALIAS', 'ttl': 600, 'value': ''}
) )
self.assertNotIn('value', a.data)
self.assertIsNone(a.data['value'])
def test_record_new(self): def test_record_new(self):
txt = Record.new( txt = Record.new(
@ -251,6 +251,20 @@ class TestRecord(TestCase):
Record.new(self.zone, 'unknown', {'type': 'XXX'}) Record.new(self.zone, 'unknown', {'type': 'XXX'})
self.assertTrue('Unknown record type' in str(ctx.exception)) self.assertTrue('Unknown record type' in str(ctx.exception))
def test_record_new_with_values_and_value(self):
a = Record.new(
self.zone,
'a',
{
'ttl': 44,
'type': 'A',
'value': '1.2.3.4',
'values': ['2.3.4.5', '3.4.5.6'],
},
)
# values is preferred over value when both exist
self.assertEqual(['2.3.4.5', '3.4.5.6'], a.values)
def test_record_copy(self): def test_record_copy(self):
a = Record.new( a = Record.new(
self.zone, 'a', {'ttl': 44, 'type': 'A', 'value': '1.2.3.4'} self.zone, 'a', {'ttl': 44, 'type': 'A', 'value': '1.2.3.4'}
@ -338,6 +352,27 @@ class TestRecord(TestCase):
del b._octodns['key']['second'] del b._octodns['key']['second']
self.assertNotEqual(a.data, b.data) self.assertNotEqual(a.data, b.data)
def test_record_copy_with_no_values(self):
txt = Record.new(
self.zone,
'txt',
{'ttl': 45, 'type': 'TXT', 'values': []},
lenient=True,
)
dup = txt.copy()
self.assertEqual(txt.values, dup.values)
cname = Record.new(
self.zone,
'cname',
{'ttl': 45, 'type': 'CNAME', 'value': ''},
lenient=True,
)
dup = cname.copy()
self.assertEqual(cname.value, dup.value)
def test_change(self): def test_change(self):
existing = Record.new( existing = Record.new(
self.zone, 'txt', {'ttl': 44, 'type': 'TXT', 'value': 'some text'} self.zone, 'txt', {'ttl': 44, 'type': 'TXT', 'value': 'some text'}
@ -631,10 +666,9 @@ class TestRecordValidation(TestCase):
lenient=True, lenient=True,
) )
# __init__ may still blow up, even if validation is lenient
with self.assertRaises(KeyError) as ctx:
Record.new(self.zone, 'www', {'type': 'A', 'ttl': -1}, lenient=True)
self.assertEqual(('value',), ctx.exception.args)
# empty values is allowed with lenient
r = Record.new(self.zone, 'www', {'type': 'A', 'ttl': -1}, lenient=True)
self.assertEqual([], r.values)
# no exception if we're in lenient mode from config # no exception if we're in lenient mode from config
Record.new( Record.new(


Loading…
Cancel
Save