1
0
mirror of https://github.com/github/octodns.git synced 2024-05-11 05:55:00 +00:00

Treat value/values interchangably when configuring records

- All of the `if data isn't a list turn it into one in the value type
  validates are no longer needed, they'll always be passed a list now`
- Special case to handle PTR/target values since it previously was
  single value and is now values

See README for more information
This commit is contained in:
Ross McFarland
2024-01-15 16:02:35 -08:00
parent 3beab65e8b
commit c04a320cfd
12 changed files with 52 additions and 63 deletions

View File

@@ -8,9 +8,12 @@
* Support added for config env variable expansion on nested levels, not just
top-level provider/processor keys
* _ChunkedValue ASCII validation added, SPF & TXT
* Minor validation improvement to catch cases where Record values is provided a
single a-single-value by accident instead of a list, a common type-o that
can results in each character being a value in the resulting Record data
* Re-work value/values handling to always try and do the "right" thing based on
the content, so both singular values and lists will be handled identically
regardless of whether the key is value or values. This may result in
changes/fixes on the first sync after updating IFF you currently have
`values: a-single-thing`, which would have previously been pushed up as bunch
of single character values.
## v1.4.0 - 2023-12-04 - Minor Meta

View File

@@ -277,14 +277,8 @@ class ValuesMixin(object):
def validate(cls, name, fqdn, data):
reasons = super().validate(name, fqdn, data)
try:
values = data['values']
if isinstance(values, str):
reasons.append(
f'single value provided under values key, "{values}"'
)
except KeyError:
values = data.get('value', [])
values = data.get('values', data.get('value', []))
values = values if isinstance(values, (list, tuple)) else [values]
reasons.extend(cls._value_type.validate(values, cls._type))
@@ -300,13 +294,9 @@ class ValuesMixin(object):
def __init__(self, zone, name, data, source=None, context=None):
super().__init__(zone, name, data, source=source, context=context)
try:
values = data['values']
except KeyError:
try:
values = [data['value']]
except KeyError:
values = []
values = data.get('values', data.get('value', []))
values = values if isinstance(values, (list, tuple)) else [values]
self.values = sorted(self._value_type.process(values))
def changes(self, other, target):

View File

@@ -26,8 +26,6 @@ class CaaValue(EqualityTupleMixin, dict):
@classmethod
def validate(cls, data, _type):
if not isinstance(data, (list, tuple)):
data = (data,)
reasons = []
for value in data:
try:

View File

@@ -110,8 +110,6 @@ class LocValue(EqualityTupleMixin, dict):
direction_keys = ['lat_direction', 'long_direction']
if not isinstance(data, (list, tuple)):
data = (data,)
reasons = []
for value in data:
for key in int_keys:

View File

@@ -26,8 +26,6 @@ class MxValue(EqualityTupleMixin, dict):
@classmethod
def validate(cls, data, _type):
if not isinstance(data, (list, tuple)):
data = (data,)
reasons = []
for value in data:
try:

View File

@@ -43,8 +43,6 @@ class NaptrValue(EqualityTupleMixin, dict):
@classmethod
def validate(cls, data, _type):
if not isinstance(data, (list, tuple)):
data = (data,)
reasons = []
for value in data:
try:

View File

@@ -41,8 +41,6 @@ class SrvValue(EqualityTupleMixin, dict):
@classmethod
def validate(cls, data, _type):
if not isinstance(data, (list, tuple)):
data = (data,)
reasons = []
for value in data:
# TODO: validate algorithm and fingerprint_type values

View File

@@ -34,8 +34,6 @@ class SshfpValue(EqualityTupleMixin, dict):
@classmethod
def validate(cls, data, _type):
if not isinstance(data, (list, tuple)):
data = (data,)
reasons = []
for value in data:
try:

View File

@@ -51,10 +51,8 @@ class _TargetsValue(str):
@classmethod
def validate(cls, data, _type):
if not data:
if not data or all(not d for d in data):
return ['missing value(s)']
elif not isinstance(data, (list, tuple)):
data = (data,)
reasons = []
for value in data:
value = idna_encode(value)

View File

@@ -41,8 +41,6 @@ class TlsaValue(EqualityTupleMixin, dict):
@classmethod
def validate(cls, data, _type):
if not isinstance(data, (list, tuple)):
data = (data,)
reasons = []
for value in data:
try:

View File

@@ -13,8 +13,6 @@ class UrlfwdValue(EqualityTupleMixin, dict):
@classmethod
def validate(cls, data, _type):
if not isinstance(data, (list, tuple)):
data = (data,)
reasons = []
for value in data:
try:

View File

@@ -683,42 +683,56 @@ class TestRecordValidation(TestCase):
lenient=True,
)
def test_values_is_single_value(self):
with self.assertRaises(ValidationError) as ctx:
Record.new(
self.zone,
'thing',
{'type': 'TXT', 'ttl': 42, 'values': 'just one'},
)
self.assertEqual(
['single value provided under values key, "just one"'],
ctx.exception.reasons,
)
# same thing is fine as `value`
txt = Record.new(
def test_values_and_value(self):
# value w/one
r = Record.new(
self.zone, 'thing', {'type': 'TXT', 'ttl': 42, 'value': 'just one'}
)
self.assertEqual(1, len(txt.values))
self.assertEqual('just one', txt.values[0])
self.assertEqual(['just one'], r.values)
# same thing is fine when a list
txt = Record.new(
# value w/multiple
r = Record.new(
self.zone,
'thing',
{'type': 'TXT', 'ttl': 42, 'values': ['just one']},
{'type': 'TXT', 'ttl': 42, 'value': ['the first', 'the second']},
)
self.assertEqual(1, len(txt.values))
self.assertEqual('just one', txt.values[0])
self.assertEqual(['the first', 'the second'], r.values)
# or tuple
txt = Record.new(
# values w/one
r = Record.new(
self.zone, 'thing', {'type': 'TXT', 'ttl': 42, 'values': 'just one'}
)
self.assertEqual(['just one'], r.values)
# values w/multiple
r = Record.new(
self.zone,
'thing',
{'type': 'TXT', 'ttl': 42, 'values': ('just one',)},
{'type': 'TXT', 'ttl': 42, 'values': ['the first', 'the second']},
)
self.assertEqual(1, len(txt.values))
self.assertEqual('just one', txt.values[0])
self.assertEqual(['the first', 'the second'], r.values)
# tuples work too
r = Record.new(
self.zone,
'thing',
{'type': 'TXT', 'ttl': 42, 'values': ('the first', 'the second')},
)
self.assertEqual(['the first', 'the second'], r.values)
# values is preferred over value
# values w/multiple
r = Record.new(
self.zone,
'thing',
{
'type': 'TXT',
'ttl': 42,
'values': ['the first', 'the second'],
'value': ['not used', 'not used'],
},
)
self.assertEqual(['the first', 'the second'], r.values)
def test_validation_context(self):
# fails validation, no context