mirror of
https://github.com/github/octodns.git
synced 2024-05-11 05:55:00 +00:00
Progress towards fully fleshed out pools & rules
This commit is contained in:
@@ -469,11 +469,43 @@ class _DynamicMixin(object):
|
||||
except KeyError:
|
||||
pools = {}
|
||||
|
||||
if not pools:
|
||||
if not isinstance(pools, dict):
|
||||
reasons.append('pools must be a dict')
|
||||
elif not pools:
|
||||
reasons.append('missing pools')
|
||||
else:
|
||||
for pool in sorted(pools.values()):
|
||||
reasons.extend(cls._value_type.validate(pool, cls))
|
||||
for _id, pool in sorted(pools.items()):
|
||||
if not isinstance(pool, dict):
|
||||
reasons.append('pool "{}" must be a dict'.format(_id))
|
||||
continue
|
||||
try:
|
||||
values = pool['values']
|
||||
except KeyError:
|
||||
reasons.append('pool "{}" is missing values'.format(_id))
|
||||
continue
|
||||
|
||||
for value_num, value in enumerate(values):
|
||||
value_num += 1
|
||||
try:
|
||||
weight = value['weight']
|
||||
weight = int(weight)
|
||||
if weight < 1 or weight > 255:
|
||||
reasons.append('invalid weight "{}" in pool "{}" '
|
||||
'value {}'.format(weight, _id,
|
||||
value_num))
|
||||
except KeyError:
|
||||
pass
|
||||
except ValueError:
|
||||
reasons.append('invalid weight "{}" in pool "{}" '
|
||||
'value {}'.format(weight, _id,
|
||||
value_num))
|
||||
|
||||
try:
|
||||
value = value['value']
|
||||
reasons.extend(cls._value_type.validate(value, cls))
|
||||
except KeyError:
|
||||
reasons.append('missing value in pool "{}" '
|
||||
'value {}'.format(_id, value_num))
|
||||
|
||||
try:
|
||||
rules = data['dynamic']['rules']
|
||||
@@ -488,26 +520,19 @@ class _DynamicMixin(object):
|
||||
for rule_num, rule in enumerate(rules):
|
||||
rule_num += 1
|
||||
try:
|
||||
rule_pools = rule['pools']
|
||||
pool = rule['pool']
|
||||
except KeyError:
|
||||
rule_pools = {}
|
||||
if not rule_pools:
|
||||
reasons.append('rule {} missing pools'.format(rule_num))
|
||||
elif not isinstance(rule_pools, dict):
|
||||
reasons.append('rule {} pools must be a dict'
|
||||
.format(rule_num))
|
||||
else:
|
||||
for weight, pool in rule_pools.items():
|
||||
try:
|
||||
weight = int(weight)
|
||||
if weight < 1 or weight > 255:
|
||||
reasons.append('invalid pool weight "{}"'
|
||||
.format(weight))
|
||||
except ValueError:
|
||||
reasons.append('invalid pool weight "{}"'
|
||||
.format(weight))
|
||||
if pool not in pools:
|
||||
reasons.append('undefined pool "{}"'.format(pool))
|
||||
reasons.append('rule {} missing pool'.format(rule_num))
|
||||
continue
|
||||
|
||||
if not isinstance(pool, basestring):
|
||||
reasons.append('rule {} invalid pool "{}"'
|
||||
.format(rule_num, pool))
|
||||
elif pool not in pools:
|
||||
reasons.append('rule {} undefined pool "{}"'
|
||||
.format(rule_num, pool))
|
||||
|
||||
# TODO: validate GEOs if present
|
||||
|
||||
return reasons
|
||||
|
||||
@@ -575,6 +600,7 @@ class _DynamicMixin(object):
|
||||
class Ipv4List(object):
|
||||
|
||||
@classmethod
|
||||
# TODO: remove record_cls it's redudant (cls)
|
||||
def validate(cls, data, record_cls):
|
||||
if not isinstance(data, (list, tuple)):
|
||||
data = (data,)
|
||||
|
@@ -2,29 +2,35 @@
|
||||
a:
|
||||
dynamic:
|
||||
pools:
|
||||
ams: 1.1.1.1
|
||||
ams:
|
||||
# TODO: make value possible
|
||||
values:
|
||||
- value: 1.1.1.1
|
||||
iad:
|
||||
- 2.2.2.2
|
||||
- 3.3.3.3
|
||||
lax: 4.4.4.4
|
||||
sea: 5.5.5.5
|
||||
values:
|
||||
# TODO: make value optional
|
||||
- value: 2.2.2.2
|
||||
- value: 3.3.3.3
|
||||
lax:
|
||||
values:
|
||||
- value: 4.4.4.4
|
||||
sea:
|
||||
values:
|
||||
- value: 6.6.6.6
|
||||
weight: 10
|
||||
- value: 5.5.5.5
|
||||
weight: 25
|
||||
rules:
|
||||
- geo: EU-UK
|
||||
pools:
|
||||
10: iad
|
||||
pool: iad
|
||||
- geo: EU
|
||||
pools:
|
||||
10: ams
|
||||
10: iad
|
||||
pool: ams
|
||||
- geos:
|
||||
- NA-US-CA
|
||||
- NA-US-OR
|
||||
- NA-US-WA
|
||||
pools:
|
||||
25: iad
|
||||
75: sea
|
||||
- pools:
|
||||
10: iad
|
||||
pool: sea
|
||||
- pool: iad
|
||||
type: A
|
||||
values:
|
||||
- 2.2.2.2
|
||||
@@ -32,29 +38,33 @@ a:
|
||||
aaaa:
|
||||
dynamic:
|
||||
pools:
|
||||
ams: 2601:642:500:e210:62f8:1dff:feb8:9471
|
||||
ams:
|
||||
values:
|
||||
- value: 2601:642:500:e210:62f8:1dff:feb8:9471
|
||||
iad:
|
||||
- 2601:642:500:e210:62f8:1dff:feb8:9472
|
||||
- 2601:642:500:e210:62f8:1dff:feb8:9473
|
||||
lax: 2601:642:500:e210:62f8:1dff:feb8:9474
|
||||
sea: 2601:642:500:e210:62f8:1dff:feb8:9475
|
||||
values:
|
||||
- value: 2601:642:500:e210:62f8:1dff:feb8:9472
|
||||
- value: 2601:642:500:e210:62f8:1dff:feb8:9473
|
||||
lax:
|
||||
values:
|
||||
- value: 2601:642:500:e210:62f8:1dff:feb8:9474
|
||||
sea:
|
||||
values:
|
||||
- value: 2601:642:500:e210:62f8:1dff:feb8:9475
|
||||
weight: 1
|
||||
- value: 2601:642:500:e210:62f8:1dff:feb8:9476
|
||||
weight: 2
|
||||
rules:
|
||||
- geo: EU-UK
|
||||
pools:
|
||||
10: iad
|
||||
pool: iad
|
||||
- geo: EU
|
||||
pools:
|
||||
10: ams
|
||||
10: iad
|
||||
pool: ams
|
||||
- geos:
|
||||
- NA-US-CA
|
||||
- NA-US-OR
|
||||
- NA-US-WA
|
||||
pools:
|
||||
25: iad
|
||||
75: sea
|
||||
- pools:
|
||||
10: iad
|
||||
pool: sea
|
||||
- pool: iad
|
||||
type: AAAA
|
||||
values:
|
||||
- 2601:642:500:e210:62f8:1dff:feb8:947a
|
||||
@@ -62,38 +72,120 @@ aaaa:
|
||||
cname:
|
||||
dynamic:
|
||||
pools:
|
||||
ams: target-ams.unit.tests.
|
||||
iad: target-iad.unit.tests.
|
||||
lax: target-lax.unit.tests.
|
||||
sea: target-sea.unit.tests.
|
||||
ams:
|
||||
values:
|
||||
- value: target-ams.unit.tests.
|
||||
iad:
|
||||
values:
|
||||
- value: target-iad.unit.tests.
|
||||
lax:
|
||||
values:
|
||||
- value: target-lax.unit.tests.
|
||||
sea:
|
||||
values:
|
||||
- value: target-sea-1.unit.tests.
|
||||
weight: 100
|
||||
- value: target-sea-2.unit.tests.
|
||||
weight: 175
|
||||
rules:
|
||||
- geo: EU-UK
|
||||
pools:
|
||||
10: iad
|
||||
pool: iad
|
||||
- geo: EU
|
||||
pools:
|
||||
10: ams
|
||||
10: iad
|
||||
pool: ams
|
||||
- geos:
|
||||
- NA-US-CA
|
||||
- NA-US-OR
|
||||
- NA-US-WA
|
||||
pools:
|
||||
12: sea
|
||||
250: iad
|
||||
- pools:
|
||||
1: sea
|
||||
4: iad
|
||||
pool: sea
|
||||
- pool: iad
|
||||
type: CNAME
|
||||
value: target.unit.tests.
|
||||
real-ish-a:
|
||||
dynamic:
|
||||
pools:
|
||||
ap-southeast-1:
|
||||
values:
|
||||
# ap-southeast-1a
|
||||
- value: 1.4.1.1
|
||||
weight: 2
|
||||
- value: 1.4.1.2
|
||||
weight: 2
|
||||
# ap-southeast-1b
|
||||
- value: 1.4.2.1
|
||||
- value: 1.4.2.2
|
||||
# ap-southeast-1c
|
||||
- value: 1.4.3.1
|
||||
- value: 1.4.3.2
|
||||
eu-central-1:
|
||||
values:
|
||||
# eu-central-1a
|
||||
- value: 1.3.1.1
|
||||
- value: 1.3.1.2
|
||||
# eu-central-1b
|
||||
- value: 1.3.2.1
|
||||
- value: 1.3.2.2
|
||||
# eu-central-1c
|
||||
- value: 1.3.3.1
|
||||
- value: 1.3.3.2
|
||||
us-east-1:
|
||||
values:
|
||||
# us-east-1a
|
||||
- value: 1.1.1.1
|
||||
- value: 1.1.1.2
|
||||
# us-east-1b
|
||||
- value: 1.1.2.1
|
||||
- value: 1.1.2.2
|
||||
# us-east-1c
|
||||
- value: 1.1.3.1
|
||||
- value: 1.1.3.2
|
||||
us-west-2:
|
||||
values:
|
||||
# us-west-2a
|
||||
- value: 1.2.1.1
|
||||
- value: 1.2.1.2
|
||||
# us-west-2b
|
||||
- value: 1.2.2.1
|
||||
- value: 1.2.2.2
|
||||
# us-west-2c
|
||||
- value: 1.2.3.1
|
||||
- value: 1.2.3.2
|
||||
rules:
|
||||
- geos:
|
||||
# TODO: require sorted
|
||||
- NA-US-CA
|
||||
- NA-US-OR
|
||||
- NA-US-WA
|
||||
pool: us-west-2
|
||||
- geos:
|
||||
- AS-CN
|
||||
pool: ap-southeast-1
|
||||
- geos:
|
||||
- AF
|
||||
- EU
|
||||
pool: eu-central-1
|
||||
- pool: us-east-1
|
||||
type: A
|
||||
values:
|
||||
# Generally these should match the values of your "default" rule's pools as
|
||||
# if everything fails healthchecks they'll fallback to this
|
||||
- 1.1.1.1
|
||||
- 1.1.1.2
|
||||
- 1.1.2.1
|
||||
- 1.1.2.2
|
||||
- 1.1.3.1
|
||||
- 1.1.3.2
|
||||
simple-weighted:
|
||||
dynamic:
|
||||
pools:
|
||||
one: one.unit.tests.
|
||||
two: two.unit.tests.
|
||||
default:
|
||||
values:
|
||||
- value: one.unit.tests.
|
||||
weight: 3
|
||||
- value: two.unit.tests.
|
||||
weight: 2
|
||||
rules:
|
||||
- pools:
|
||||
100: one
|
||||
200: two
|
||||
- pool: default
|
||||
type: CNAME
|
||||
# CNAMEs don't support health checks (currently) so these will never be used
|
||||
# on providers with dynamic support
|
||||
value: default.unit.tests.
|
||||
|
@@ -34,7 +34,7 @@ class TestYamlProvider(TestCase):
|
||||
self.assertEquals(18, len(zone.records))
|
||||
|
||||
source.populate(dynamic_zone)
|
||||
self.assertEquals(4, len(dynamic_zone.records))
|
||||
self.assertEquals(5, len(dynamic_zone.records))
|
||||
|
||||
# Assumption here is that a clean round-trip means that everything
|
||||
# worked as expected, data that went in came back out and could be
|
||||
@@ -64,11 +64,11 @@ class TestYamlProvider(TestCase):
|
||||
|
||||
# Dynamic plan
|
||||
plan = target.plan(dynamic_zone)
|
||||
self.assertEquals(4, len(filter(lambda c: isinstance(c, Create),
|
||||
self.assertEquals(5, len(filter(lambda c: isinstance(c, Create),
|
||||
plan.changes)))
|
||||
self.assertFalse(isfile(dynamic_yaml_file))
|
||||
# Apply it
|
||||
self.assertEquals(4, target.apply(plan))
|
||||
self.assertEquals(5, target.apply(plan))
|
||||
self.assertTrue(isfile(dynamic_yaml_file))
|
||||
|
||||
# There should be no changes after the round trip
|
||||
@@ -133,6 +133,10 @@ class TestYamlProvider(TestCase):
|
||||
self.assertTrue('value' in dyna)
|
||||
# self.assertTrue('dynamic' in dyna)
|
||||
|
||||
dyna = data.pop('real-ish-a')
|
||||
self.assertTrue('values' in dyna)
|
||||
# self.assertTrue('dynamic' in dyna)
|
||||
|
||||
dyna = data.pop('simple-weighted')
|
||||
self.assertTrue('value' in dyna)
|
||||
# self.assertTrue('dynamic' in dyna)
|
||||
|
@@ -1900,17 +1900,36 @@ class TestDynamicRecords(TestCase):
|
||||
a_data = {
|
||||
'dynamic': {
|
||||
'pools': {
|
||||
'one': '3.3.3.3',
|
||||
'two': [
|
||||
'4.4.4.4',
|
||||
'5.5.5.5',
|
||||
],
|
||||
'one': {
|
||||
'values': [{
|
||||
'value': '3.3.3.3',
|
||||
}],
|
||||
},
|
||||
'two': {
|
||||
'values': [{
|
||||
'value': '4.4.4.4',
|
||||
}, {
|
||||
'value': '5.5.5.5',
|
||||
}],
|
||||
},
|
||||
'three': {
|
||||
'values': [{
|
||||
'weight': 10,
|
||||
'value': '4.4.4.4',
|
||||
}, {
|
||||
'weight': 12,
|
||||
'value': '5.5.5.5',
|
||||
}],
|
||||
},
|
||||
},
|
||||
'rules': [{
|
||||
'pools': {
|
||||
100: 'one',
|
||||
200: 'two',
|
||||
}
|
||||
'geos': ['AF', 'EU'],
|
||||
'pool': 'three',
|
||||
}, {
|
||||
'geos': ['NA-US-CA'],
|
||||
'pool': 'two',
|
||||
}, {
|
||||
'pool': 'one',
|
||||
}],
|
||||
},
|
||||
'ttl': 60,
|
||||
@@ -1931,6 +1950,8 @@ class TestDynamicRecords(TestCase):
|
||||
self.assertTrue(pools)
|
||||
self.assertEquals(a_data['dynamic']['pools']['one'], pools['one'].data)
|
||||
self.assertEquals(a_data['dynamic']['pools']['two'], pools['two'].data)
|
||||
self.assertEquals(a_data['dynamic']['pools']['three'],
|
||||
pools['three'].data)
|
||||
|
||||
rules = dynamic.rules
|
||||
self.assertTrue(rules)
|
||||
@@ -1945,12 +1966,58 @@ class TestDynamicRecords(TestCase):
|
||||
'2601:642:500:e210:62f8:1dff:feb8:9474',
|
||||
'2601:642:500:e210:62f8:1dff:feb8:9475',
|
||||
],
|
||||
'three': {
|
||||
1: '2601:642:500:e210:62f8:1dff:feb8:9476',
|
||||
2: '2601:642:500:e210:62f8:1dff:feb8:9477',
|
||||
},
|
||||
},
|
||||
'rules': [{
|
||||
'pools': {
|
||||
100: 'one',
|
||||
200: 'two',
|
||||
'pools': [
|
||||
'three',
|
||||
'two',
|
||||
'one',
|
||||
],
|
||||
}],
|
||||
},
|
||||
'ttl': 60,
|
||||
'values': [
|
||||
'2601:642:500:e210:62f8:1dff:feb8:9471',
|
||||
'2601:642:500:e210:62f8:1dff:feb8:9472',
|
||||
],
|
||||
}
|
||||
aaaa_data = {
|
||||
'dynamic': {
|
||||
'pools': {
|
||||
'one': {
|
||||
'values': [{
|
||||
'value': '2601:642:500:e210:62f8:1dff:feb8:9473',
|
||||
}],
|
||||
},
|
||||
'two': {
|
||||
'values': [{
|
||||
'value': '2601:642:500:e210:62f8:1dff:feb8:9474',
|
||||
}, {
|
||||
'value': '2601:642:500:e210:62f8:1dff:feb8:9475',
|
||||
}],
|
||||
},
|
||||
'three': {
|
||||
'values': [{
|
||||
'weight': 10,
|
||||
'value': '2601:642:500:e210:62f8:1dff:feb8:9476',
|
||||
}, {
|
||||
'weight': 12,
|
||||
'value': '2601:642:500:e210:62f8:1dff:feb8:9477',
|
||||
}],
|
||||
},
|
||||
},
|
||||
'rules': [{
|
||||
'geos': ['AF', 'EU'],
|
||||
'pool': 'three',
|
||||
}, {
|
||||
'geos': ['NA-US-CA'],
|
||||
'pool': 'two',
|
||||
}, {
|
||||
'pool': 'one',
|
||||
}],
|
||||
},
|
||||
'ttl': 60,
|
||||
@@ -1973,6 +2040,8 @@ class TestDynamicRecords(TestCase):
|
||||
pools['one'].data)
|
||||
self.assertEquals(aaaa_data['dynamic']['pools']['two'],
|
||||
pools['two'].data)
|
||||
self.assertEquals(aaaa_data['dynamic']['pools']['three'],
|
||||
pools['three'].data)
|
||||
|
||||
rules = dynamic.rules
|
||||
self.assertTrue(rules)
|
||||
@@ -1982,14 +2051,34 @@ class TestDynamicRecords(TestCase):
|
||||
cname_data = {
|
||||
'dynamic': {
|
||||
'pools': {
|
||||
'one': 'one.cname.target.',
|
||||
'two': 'two.cname.target.',
|
||||
'one': {
|
||||
'values': [{
|
||||
'value': 'one.cname.target.',
|
||||
}],
|
||||
},
|
||||
'two': {
|
||||
'values': [{
|
||||
'value': 'two.cname.target.',
|
||||
}],
|
||||
},
|
||||
'three': {
|
||||
'values': [{
|
||||
'weight': 12,
|
||||
'value': 'three-1.cname.target.',
|
||||
}, {
|
||||
'weight': 32,
|
||||
'value': 'three-2.cname.target.',
|
||||
}]
|
||||
},
|
||||
},
|
||||
'rules': [{
|
||||
'pools': {
|
||||
100: 'one',
|
||||
200: 'two',
|
||||
}
|
||||
'geos': ['AF', 'EU'],
|
||||
'pool': 'three',
|
||||
}, {
|
||||
'geos': ['NA-US-CA'],
|
||||
'pool': 'two',
|
||||
}, {
|
||||
'pool': 'one',
|
||||
}],
|
||||
},
|
||||
'ttl': 60,
|
||||
@@ -2009,6 +2098,8 @@ class TestDynamicRecords(TestCase):
|
||||
pools['one'].data)
|
||||
self.assertEquals(cname_data['dynamic']['pools']['two'],
|
||||
pools['two'].data)
|
||||
self.assertEquals(cname_data['dynamic']['pools']['three'],
|
||||
pools['three'].data)
|
||||
|
||||
rules = dynamic.rules
|
||||
self.assertTrue(rules)
|
||||
@@ -2019,9 +2110,7 @@ class TestDynamicRecords(TestCase):
|
||||
a_data = {
|
||||
'dynamic': {
|
||||
'rules': [{
|
||||
'pools': {
|
||||
1: 'one',
|
||||
}
|
||||
'pool': 'one',
|
||||
}],
|
||||
},
|
||||
'ttl': 60,
|
||||
@@ -2033,7 +2122,7 @@ class TestDynamicRecords(TestCase):
|
||||
}
|
||||
with self.assertRaises(ValidationError) as ctx:
|
||||
Record.new(self.zone, 'bad', a_data)
|
||||
self.assertEquals(['missing pools', 'undefined pool "one"'],
|
||||
self.assertEquals(['missing pools', 'rule 1 undefined pool "one"'],
|
||||
ctx.exception.reasons)
|
||||
|
||||
# Empty pools
|
||||
@@ -2042,9 +2131,7 @@ class TestDynamicRecords(TestCase):
|
||||
'pools': {
|
||||
},
|
||||
'rules': [{
|
||||
'pools': {
|
||||
1: 'one',
|
||||
}
|
||||
'pool': 'one',
|
||||
}],
|
||||
},
|
||||
'ttl': 60,
|
||||
@@ -2056,24 +2143,64 @@ class TestDynamicRecords(TestCase):
|
||||
}
|
||||
with self.assertRaises(ValidationError) as ctx:
|
||||
Record.new(self.zone, 'bad', a_data)
|
||||
self.assertEquals(['missing pools', 'undefined pool "one"'],
|
||||
self.assertEquals(['missing pools', 'rule 1 undefined pool "one"'],
|
||||
ctx.exception.reasons)
|
||||
|
||||
# pools not a dict
|
||||
a_data = {
|
||||
'dynamic': {
|
||||
'pools': [],
|
||||
'rules': [{
|
||||
'pool': 'one',
|
||||
}],
|
||||
},
|
||||
'ttl': 60,
|
||||
'type': 'A',
|
||||
'values': [
|
||||
'1.1.1.1',
|
||||
'2.2.2.2',
|
||||
],
|
||||
}
|
||||
with self.assertRaises(ValidationError) as ctx:
|
||||
Record.new(self.zone, 'bad', a_data)
|
||||
self.assertEquals(['pools must be a dict',
|
||||
'rule 1 undefined pool "one"'],
|
||||
ctx.exception.reasons)
|
||||
|
||||
# Invalid addresses
|
||||
a_data = {
|
||||
'dynamic': {
|
||||
'pools': {
|
||||
'one': 'this-aint-right',
|
||||
'two': [
|
||||
'4.4.4.4',
|
||||
'nor-is-this',
|
||||
],
|
||||
'one': {
|
||||
'values': [{
|
||||
'value': 'this-aint-right',
|
||||
}],
|
||||
},
|
||||
'two': {
|
||||
'values': [{
|
||||
'value': '4.4.4.4',
|
||||
}, {
|
||||
'value': 'nor-is-this',
|
||||
}]
|
||||
},
|
||||
'three': {
|
||||
'values': [{
|
||||
'weight': 1,
|
||||
'value': '5.5.5.5',
|
||||
}, {
|
||||
'weight': 2,
|
||||
'value': 'yet-another-bad-one',
|
||||
}],
|
||||
},
|
||||
},
|
||||
'rules': [{
|
||||
'pools': {
|
||||
100: 'one',
|
||||
200: 'two',
|
||||
}
|
||||
'geos': ['AF', 'EU'],
|
||||
'pool': 'three',
|
||||
}, {
|
||||
'geos': ['NA-US-CA'],
|
||||
'pool': 'two',
|
||||
}, {
|
||||
'pool': 'one',
|
||||
}],
|
||||
},
|
||||
'ttl': 60,
|
||||
@@ -2085,25 +2212,42 @@ class TestDynamicRecords(TestCase):
|
||||
}
|
||||
with self.assertRaises(ValidationError) as ctx:
|
||||
Record.new(self.zone, 'bad', a_data)
|
||||
self.assertEquals(['invalid IPv4 address "nor-is-this"',
|
||||
'invalid IPv4 address "this-aint-right"'],
|
||||
ctx.exception.reasons)
|
||||
self.assertEquals([
|
||||
'invalid IPv4 address "this-aint-right"',
|
||||
'invalid IPv4 address "yet-another-bad-one"',
|
||||
'invalid IPv4 address "nor-is-this"',
|
||||
], ctx.exception.reasons)
|
||||
|
||||
# missing value(s)
|
||||
a_data = {
|
||||
'dynamic': {
|
||||
'pools': {
|
||||
'one': [],
|
||||
'two': [
|
||||
'3.3.3.3',
|
||||
'4.4.4.4',
|
||||
],
|
||||
'one': {},
|
||||
'two': {
|
||||
'values': [{
|
||||
'value': '4.4.4.4',
|
||||
}, {
|
||||
'value': '5.5.5.5',
|
||||
}]
|
||||
},
|
||||
'three': {
|
||||
'values': [{
|
||||
'weight': 1,
|
||||
'value': '6.6.6.6',
|
||||
}, {
|
||||
'weight': 2,
|
||||
'value': '7.7.7.7',
|
||||
}],
|
||||
},
|
||||
},
|
||||
'rules': [{
|
||||
'pools': {
|
||||
100: 'one',
|
||||
200: 'two',
|
||||
}
|
||||
'geos': ['AF', 'EU'],
|
||||
'pool': 'three',
|
||||
}, {
|
||||
'geos': ['NA-US-CA'],
|
||||
'pool': 'two',
|
||||
}, {
|
||||
'pool': 'one',
|
||||
}],
|
||||
},
|
||||
'ttl': 60,
|
||||
@@ -2115,23 +2259,39 @@ class TestDynamicRecords(TestCase):
|
||||
}
|
||||
with self.assertRaises(ValidationError) as ctx:
|
||||
Record.new(self.zone, 'bad', a_data)
|
||||
self.assertEquals(['missing value(s)'], ctx.exception.reasons)
|
||||
self.assertEquals(['pool "one" is missing values'],
|
||||
ctx.exception.reasons)
|
||||
|
||||
# Empty value
|
||||
# pool valu not a dict
|
||||
a_data = {
|
||||
'dynamic': {
|
||||
'pools': {
|
||||
'one': '',
|
||||
'two': [
|
||||
'3.3.3.3',
|
||||
'blip',
|
||||
],
|
||||
'two': {
|
||||
'values': [{
|
||||
'value': '4.4.4.4',
|
||||
}, {
|
||||
'value': '5.5.5.5',
|
||||
}]
|
||||
},
|
||||
'three': {
|
||||
'values': [{
|
||||
'weight': 1,
|
||||
'value': '6.6.6.6',
|
||||
}, {
|
||||
'weight': 2,
|
||||
'value': '7.7.7.7',
|
||||
}],
|
||||
},
|
||||
},
|
||||
'rules': [{
|
||||
'pools': {
|
||||
100: 'one',
|
||||
200: 'two',
|
||||
}
|
||||
'geos': ['AF', 'EU'],
|
||||
'pool': 'three',
|
||||
}, {
|
||||
'geos': ['NA-US-CA'],
|
||||
'pool': 'two',
|
||||
}, {
|
||||
'pool': 'one',
|
||||
}],
|
||||
},
|
||||
'ttl': 60,
|
||||
@@ -2143,24 +2303,178 @@ class TestDynamicRecords(TestCase):
|
||||
}
|
||||
with self.assertRaises(ValidationError) as ctx:
|
||||
Record.new(self.zone, 'bad', a_data)
|
||||
self.assertEquals(['invalid IPv4 address "blip"', 'empty value'],
|
||||
self.assertEquals(['pool "one" must be a dict'],
|
||||
ctx.exception.reasons)
|
||||
|
||||
# multiple problems
|
||||
# empty pool value
|
||||
a_data = {
|
||||
'dynamic': {
|
||||
'pools': {
|
||||
'one': {},
|
||||
'two': {
|
||||
'values': [{
|
||||
'value': '4.4.4.4',
|
||||
}, {
|
||||
'value': '5.5.5.5',
|
||||
}]
|
||||
},
|
||||
'three': {
|
||||
'values': [{
|
||||
'weight': 1,
|
||||
'value': '6.6.6.6',
|
||||
}, {
|
||||
'weight': 2,
|
||||
'value': '7.7.7.7',
|
||||
}],
|
||||
},
|
||||
},
|
||||
'rules': [{
|
||||
'geos': ['AF', 'EU'],
|
||||
'pool': 'three',
|
||||
}, {
|
||||
'geos': ['NA-US-CA'],
|
||||
'pool': 'two',
|
||||
}, {
|
||||
'pool': 'one',
|
||||
}],
|
||||
},
|
||||
'ttl': 60,
|
||||
'type': 'A',
|
||||
'values': [
|
||||
'1.1.1.1',
|
||||
'2.2.2.2',
|
||||
],
|
||||
}
|
||||
with self.assertRaises(ValidationError) as ctx:
|
||||
Record.new(self.zone, 'bad', a_data)
|
||||
self.assertEquals(['pool "one" is missing values'],
|
||||
ctx.exception.reasons)
|
||||
|
||||
# invalid int weight
|
||||
a_data = {
|
||||
'dynamic': {
|
||||
'pools': {
|
||||
'one': {
|
||||
'values': [{
|
||||
'value': '3.3.3.3',
|
||||
}]
|
||||
},
|
||||
'two': {
|
||||
'values': [{
|
||||
'value': '4.4.4.4',
|
||||
}, {
|
||||
'value': '5.5.5.5',
|
||||
}]
|
||||
},
|
||||
'three': {
|
||||
'values': [{
|
||||
'weight': 1,
|
||||
'value': '6.6.6.6',
|
||||
}, {
|
||||
'weight': 256,
|
||||
'value': '7.7.7.7',
|
||||
}],
|
||||
},
|
||||
},
|
||||
'rules': [{
|
||||
'geos': ['AF', 'EU'],
|
||||
'pool': 'three',
|
||||
}, {
|
||||
'geos': ['NA-US-CA'],
|
||||
'pool': 'two',
|
||||
}, {
|
||||
'pool': 'one',
|
||||
}],
|
||||
},
|
||||
'ttl': 60,
|
||||
'type': 'A',
|
||||
'values': [
|
||||
'1.1.1.1',
|
||||
'2.2.2.2',
|
||||
],
|
||||
}
|
||||
with self.assertRaises(ValidationError) as ctx:
|
||||
Record.new(self.zone, 'bad', a_data)
|
||||
self.assertEquals(['invalid weight "256" in pool "three" value 2'],
|
||||
ctx.exception.reasons)
|
||||
|
||||
# invalid non-int weight
|
||||
a_data = {
|
||||
'dynamic': {
|
||||
'pools': {
|
||||
'one': {
|
||||
'values': [{
|
||||
'value': '3.3.3.3',
|
||||
}]
|
||||
},
|
||||
'two': {
|
||||
'values': [{
|
||||
'value': '4.4.4.4',
|
||||
}, {
|
||||
'value': '5.5.5.5',
|
||||
}]
|
||||
},
|
||||
'three': {
|
||||
'values': [{
|
||||
'weight': 1,
|
||||
'value': '6.6.6.6',
|
||||
}, {
|
||||
'weight': 'foo',
|
||||
'value': '7.7.7.7',
|
||||
}],
|
||||
},
|
||||
},
|
||||
'rules': [{
|
||||
'geos': ['AF', 'EU'],
|
||||
'pool': 'three',
|
||||
}, {
|
||||
'geos': ['NA-US-CA'],
|
||||
'pool': 'two',
|
||||
}, {
|
||||
'pool': 'one',
|
||||
}],
|
||||
},
|
||||
'ttl': 60,
|
||||
'type': 'A',
|
||||
'values': [
|
||||
'1.1.1.1',
|
||||
'2.2.2.2',
|
||||
],
|
||||
}
|
||||
with self.assertRaises(ValidationError) as ctx:
|
||||
Record.new(self.zone, 'bad', a_data)
|
||||
self.assertEquals(['invalid weight "foo" in pool "three" value 2'],
|
||||
ctx.exception.reasons)
|
||||
|
||||
# multiple pool problems
|
||||
a_data = {
|
||||
'dynamic': {
|
||||
'pools': {
|
||||
'one': '',
|
||||
'two': [
|
||||
'3.3.3.3',
|
||||
'blip',
|
||||
],
|
||||
'two': {
|
||||
'values': [{
|
||||
'value': '4.4.4.4',
|
||||
}, {
|
||||
'value': 'blip',
|
||||
}]
|
||||
},
|
||||
'three': {
|
||||
'values': [{
|
||||
'weight': 1,
|
||||
}, {
|
||||
'weight': 5000,
|
||||
'value': '7.7.7.7',
|
||||
}],
|
||||
},
|
||||
},
|
||||
'rules': [{
|
||||
'pools': {
|
||||
100: 'one',
|
||||
200: 'two',
|
||||
}
|
||||
'geos': ['AF', 'EU'],
|
||||
'pool': 'three',
|
||||
}, {
|
||||
'geos': ['NA-US-CA'],
|
||||
'pool': 'two',
|
||||
}, {
|
||||
'pool': 'one',
|
||||
}],
|
||||
},
|
||||
'ttl': 60,
|
||||
@@ -2172,14 +2486,29 @@ class TestDynamicRecords(TestCase):
|
||||
}
|
||||
with self.assertRaises(ValidationError) as ctx:
|
||||
Record.new(self.zone, 'bad', a_data)
|
||||
self.assertEquals(['invalid IPv4 address "blip"', 'empty value'],
|
||||
ctx.exception.reasons)
|
||||
self.assertEquals([
|
||||
'pool "one" must be a dict',
|
||||
'missing value in pool "three" value 1',
|
||||
'invalid weight "5000" in pool "three" value 2',
|
||||
'invalid IPv4 address "blip"',
|
||||
], ctx.exception.reasons)
|
||||
|
||||
# missing rules
|
||||
a_data = {
|
||||
'dynamic': {
|
||||
'pools': {
|
||||
'one': '1.2.3.4',
|
||||
'one': {
|
||||
'values': [{
|
||||
'value': '3.3.3.3',
|
||||
}]
|
||||
},
|
||||
'two': {
|
||||
'values': [{
|
||||
'value': '4.4.4.4',
|
||||
}, {
|
||||
'value': '5.5.5.5',
|
||||
}]
|
||||
},
|
||||
},
|
||||
},
|
||||
'ttl': 60,
|
||||
@@ -2197,7 +2526,18 @@ class TestDynamicRecords(TestCase):
|
||||
a_data = {
|
||||
'dynamic': {
|
||||
'pools': {
|
||||
'one': '1.2.3.4',
|
||||
'one': {
|
||||
'values': [{
|
||||
'value': '3.3.3.3',
|
||||
}]
|
||||
},
|
||||
'two': {
|
||||
'values': [{
|
||||
'value': '4.4.4.4',
|
||||
}, {
|
||||
'value': '5.5.5.5',
|
||||
}]
|
||||
},
|
||||
},
|
||||
'rules': [],
|
||||
},
|
||||
@@ -2216,7 +2556,18 @@ class TestDynamicRecords(TestCase):
|
||||
a_data = {
|
||||
'dynamic': {
|
||||
'pools': {
|
||||
'one': '1.2.3.4',
|
||||
'one': {
|
||||
'values': [{
|
||||
'value': '3.3.3.3',
|
||||
}]
|
||||
},
|
||||
'two': {
|
||||
'values': [{
|
||||
'value': '4.4.4.4',
|
||||
}, {
|
||||
'value': '5.5.5.5',
|
||||
}]
|
||||
},
|
||||
},
|
||||
'rules': {},
|
||||
},
|
||||
@@ -2231,13 +2582,27 @@ class TestDynamicRecords(TestCase):
|
||||
Record.new(self.zone, 'bad', a_data)
|
||||
self.assertEquals(['rules must be a list'], ctx.exception.reasons)
|
||||
|
||||
# rule without pools
|
||||
# rule without pool
|
||||
a_data = {
|
||||
'dynamic': {
|
||||
'pools': {
|
||||
'one': '1.2.3.4',
|
||||
'one': {
|
||||
'values': [{
|
||||
'value': '3.3.3.3',
|
||||
}],
|
||||
},
|
||||
'two': {
|
||||
'values': [{
|
||||
'value': '4.4.4.4',
|
||||
}, {
|
||||
'value': '5.5.5.5',
|
||||
}]
|
||||
},
|
||||
},
|
||||
'rules': [{
|
||||
'geos': ['NA-US-CA'],
|
||||
}, {
|
||||
'pool': 'one',
|
||||
}],
|
||||
},
|
||||
'ttl': 60,
|
||||
@@ -2249,16 +2614,30 @@ class TestDynamicRecords(TestCase):
|
||||
}
|
||||
with self.assertRaises(ValidationError) as ctx:
|
||||
Record.new(self.zone, 'bad', a_data)
|
||||
self.assertEquals(['rule 1 missing pools'], ctx.exception.reasons)
|
||||
self.assertEquals(['rule 1 missing pool'], ctx.exception.reasons)
|
||||
|
||||
# rule with non-dict pools
|
||||
# rule with non-string pools
|
||||
a_data = {
|
||||
'dynamic': {
|
||||
'pools': {
|
||||
'one': '1.2.3.4',
|
||||
'one': {
|
||||
'values': [{
|
||||
'value': '3.3.3.3',
|
||||
}]
|
||||
},
|
||||
'two': {
|
||||
'values': [{
|
||||
'value': '4.4.4.4',
|
||||
}, {
|
||||
'value': '5.5.5.5',
|
||||
}]
|
||||
},
|
||||
},
|
||||
'rules': [{
|
||||
'pools': ['one'],
|
||||
'geos': ['NA-US-CA'],
|
||||
'pool': [],
|
||||
}, {
|
||||
'pool': 'one',
|
||||
}],
|
||||
},
|
||||
'ttl': 60,
|
||||
@@ -2270,19 +2649,31 @@ class TestDynamicRecords(TestCase):
|
||||
}
|
||||
with self.assertRaises(ValidationError) as ctx:
|
||||
Record.new(self.zone, 'bad', a_data)
|
||||
self.assertEquals(["rule 1 pools must be a dict"],
|
||||
self.assertEquals(['rule 1 invalid pool "[]"'],
|
||||
ctx.exception.reasons)
|
||||
|
||||
# rule references non-existant pool
|
||||
a_data = {
|
||||
'dynamic': {
|
||||
'pools': {
|
||||
'one': '1.2.3.4',
|
||||
'one': {
|
||||
'values': [{
|
||||
'value': '3.3.3.3',
|
||||
}]
|
||||
},
|
||||
'two': {
|
||||
'values': [{
|
||||
'value': '4.4.4.4',
|
||||
}, {
|
||||
'value': '5.5.5.5',
|
||||
}]
|
||||
},
|
||||
},
|
||||
'rules': [{
|
||||
'pools': {
|
||||
10: 'non-existant'
|
||||
}
|
||||
'geos': ['NA-US-CA'],
|
||||
'pool': 'non-existant',
|
||||
}, {
|
||||
'pool': 'one',
|
||||
}],
|
||||
},
|
||||
'ttl': 60,
|
||||
@@ -2294,55 +2685,7 @@ class TestDynamicRecords(TestCase):
|
||||
}
|
||||
with self.assertRaises(ValidationError) as ctx:
|
||||
Record.new(self.zone, 'bad', a_data)
|
||||
self.assertEquals(["undefined pool \"non-existant\""],
|
||||
ctx.exception.reasons)
|
||||
|
||||
# invalid int weight
|
||||
a_data = {
|
||||
'dynamic': {
|
||||
'pools': {
|
||||
'one': '1.2.3.4',
|
||||
},
|
||||
'rules': [{
|
||||
'pools': {
|
||||
256: 'one'
|
||||
}
|
||||
}],
|
||||
},
|
||||
'ttl': 60,
|
||||
'type': 'A',
|
||||
'values': [
|
||||
'1.1.1.1',
|
||||
'2.2.2.2',
|
||||
],
|
||||
}
|
||||
with self.assertRaises(ValidationError) as ctx:
|
||||
Record.new(self.zone, 'bad', a_data)
|
||||
self.assertEquals(['invalid pool weight "256"'],
|
||||
ctx.exception.reasons)
|
||||
|
||||
# invalid non-int weight
|
||||
a_data = {
|
||||
'dynamic': {
|
||||
'pools': {
|
||||
'one': '1.2.3.4',
|
||||
},
|
||||
'rules': [{
|
||||
'pools': {
|
||||
'foo': 'one'
|
||||
}
|
||||
}],
|
||||
},
|
||||
'ttl': 60,
|
||||
'type': 'A',
|
||||
'values': [
|
||||
'1.1.1.1',
|
||||
'2.2.2.2',
|
||||
],
|
||||
}
|
||||
with self.assertRaises(ValidationError) as ctx:
|
||||
Record.new(self.zone, 'bad', a_data)
|
||||
self.assertEquals(['invalid pool weight "foo"'],
|
||||
self.assertEquals(["rule 1 undefined pool \"non-existant\""],
|
||||
ctx.exception.reasons)
|
||||
|
||||
def test_dynamic_lenient(self):
|
||||
|
Reference in New Issue
Block a user