Browse Source

Order changes: deletes, creates, updates

pull/882/head
Ross McFarland 4 years ago
parent
commit
cdcaef8f2b
No known key found for this signature in database GPG Key ID: 943B179E15D3B22A
4 changed files with 120 additions and 6 deletions
  1. +16
    -0
      CHANGELOG.md
  2. +6
    -6
      octodns/record/__init__.py
  3. +25
    -0
      tests/test_octodns_plan.py
  4. +73
    -0
      tests/test_octodns_record.py

+ 16
- 0
CHANGELOG.md View File

@ -1,3 +1,19 @@
## v0.9.17 - 2022-??-?? - ???
#### Noteworthy changes
* The changes in plans are now ordered based on change type prior to
considering the record name and type as was previously done. The chosen
order is: deletes, creates, updates. The reason for that many providers make
changes one at a time. When changing the type of a record, e.g. from A to
CNAME of vice versa this is done by deleting the old and creating the new.
If the CNAME create happens before the A delete it will often violate
rules against having typed records live at the same node as a CNAME. Several
providers have always handled this by sorting the changes themselves. This
just standardizes what they are doing as many other providers appear to need
to do so, but weren't. There was an ordering before, but it was essentially
arbitrarily picked.
## v0.9.16 - 2022-??-?? - ??? ## v0.9.16 - 2022-??-?? - ???
#### Noteworthy changes #### Noteworthy changes


+ 6
- 6
octodns/record/__init__.py View File

@ -15,7 +15,7 @@ from ..equality import EqualityTupleMixin
from .geo import GeoCodes from .geo import GeoCodes
class Change(object):
class Change(EqualityTupleMixin):
def __init__(self, existing, new): def __init__(self, existing, new):
self.existing = existing self.existing = existing
@ -26,14 +26,12 @@ class Change(object):
'Returns new if we have one, existing otherwise' 'Returns new if we have one, existing otherwise'
return self.new or self.existing return self.new or self.existing
def __lt__(self, other):
self_record = self.record
other_record = other.record
return ((self_record.name, self_record._type) <
(other_record.name, other_record._type))
def _equality_tuple(self):
return (self.CLASS_ORDERING, self.record.name, self.record._type)
class Create(Change): class Create(Change):
CLASS_ORDERING = 1
def __init__(self, new): def __init__(self, new):
super(Create, self).__init__(None, new) super(Create, self).__init__(None, new)
@ -44,6 +42,7 @@ class Create(Change):
class Update(Change): class Update(Change):
CLASS_ORDERING = 2
# Leader is just to allow us to work around heven eating leading whitespace # Leader is just to allow us to work around heven eating leading whitespace
# in our output. When we call this from the Manager.sync plan summary # in our output. When we call this from the Manager.sync plan summary
@ -56,6 +55,7 @@ class Update(Change):
class Delete(Change): class Delete(Change):
CLASS_ORDERING = 0
def __init__(self, existing): def __init__(self, existing):
super(Delete, self).__init__(existing, None) super(Delete, self).__init__(existing, None)


+ 25
- 0
tests/test_octodns_plan.py View File

@ -54,6 +54,31 @@ plans = [
] ]
class TestPlanSortsChanges(TestCase):
def test_plan_sorts_changes_pass_to_it(self):
# we aren't worried about the details of the sorting, that's tested in
# test_octodns_record's TestChanges. We just want to make sure that the
# changes are sorted at all.
zone = Zone('unit.tests.', [])
record_a_1 = Record.new(zone, '1', {
'type': 'A',
'ttl': 30,
'value': '1.2.3.4',
})
create_a_1 = Create(record_a_1)
record_a_2 = Record.new(zone, '2', {
'type': 'A',
'ttl': 30,
'value': '1.2.3.4',
})
create_a_2 = Create(record_a_2)
# passed in reverse of expected order
plan = Plan(None, None, [create_a_2, create_a_1], False)
self.assertEqual([create_a_1, create_a_2], plan.changes)
class TestPlanLogger(TestCase): class TestPlanLogger(TestCase):
def test_invalid_level(self): def test_invalid_level(self):


+ 73
- 0
tests/test_octodns_record.py View File

@ -5021,3 +5021,76 @@ class TestDynamicRecords(TestCase):
self.assertEqual(dynamic, dynamic) self.assertEqual(dynamic, dynamic)
self.assertNotEqual(dynamic, other) self.assertNotEqual(dynamic, other)
self.assertNotEqual(dynamic, 42) self.assertNotEqual(dynamic, 42)
class TestChanges(TestCase):
zone = Zone('unit.tests.', [])
record_a_1 = Record.new(zone, '1', {
'type': 'A',
'ttl': 30,
'value': '1.2.3.4',
})
record_a_2 = Record.new(zone, '2', {
'type': 'A',
'ttl': 30,
'value': '1.2.3.4',
})
record_aaaa_1 = Record.new(zone, '1', {
'type': 'AAAA',
'ttl': 30,
'value': '2601:644:500:e210:62f8:1dff:feb8:947a',
})
record_aaaa_2 = Record.new(zone, '2', {
'type': 'AAAA',
'ttl': 30,
'value': '2601:644:500:e210:62f8:1dff:feb8:947a',
})
def test_sort_same_change_type(self):
# expect things to be ordered by name and type since all the change
# types are the same it doesn't matter
changes = [
Create(self.record_aaaa_1),
Create(self.record_a_2),
Create(self.record_a_1),
Create(self.record_aaaa_2),
]
self.assertEqual([
Create(self.record_a_1),
Create(self.record_aaaa_1),
Create(self.record_a_2),
Create(self.record_aaaa_2),
], sorted(changes))
def test_sort_same_different_type(self):
# this time the change type is the deciding factor, deletes come before
# creates, and then updates. Things of the same type, go down the line
# and sort by name, and then type
changes = [
Delete(self.record_aaaa_1),
Create(self.record_aaaa_1),
Update(self.record_aaaa_1, self.record_aaaa_1),
Update(self.record_a_1, self.record_a_1),
Create(self.record_a_1),
Delete(self.record_a_1),
Delete(self.record_aaaa_2),
Create(self.record_aaaa_2),
Update(self.record_aaaa_2, self.record_aaaa_2),
Update(self.record_a_2, self.record_a_2),
Create(self.record_a_2),
Delete(self.record_a_2),
]
self.assertEqual([
Delete(self.record_a_1),
Delete(self.record_aaaa_1),
Delete(self.record_a_2),
Delete(self.record_aaaa_2),
Create(self.record_a_1),
Create(self.record_aaaa_1),
Create(self.record_a_2),
Create(self.record_aaaa_2),
Update(self.record_a_1, self.record_a_1),
Update(self.record_aaaa_1, self.record_aaaa_1),
Update(self.record_a_2, self.record_a_2),
Update(self.record_aaaa_2, self.record_aaaa_2),
], sorted(changes))

Loading…
Cancel
Save