Browse Source

Add Plan.meta support for making non-record changes to zones

pull/1236/head
Ross McFarland 10 months ago
parent
commit
4c48609848
No known key found for this signature in database GPG Key ID: 943B179E15D3B22A
4 changed files with 51 additions and 30 deletions
  1. +2
    -0
      CHANGELOG.md
  2. +16
    -3
      octodns/provider/base.py
  3. +27
    -22
      octodns/provider/plan.py
  4. +6
    -5
      tests/test_octodns_plan.py

+ 2
- 0
CHANGELOG.md View File

@ -8,6 +8,8 @@
the default when enforce_order=True and simple `sort`. the default when enforce_order=True and simple `sort`.
* Fix type-o in _build_kwargs handler notification * Fix type-o in _build_kwargs handler notification
* Add support for configuring OwnershipProcessor TXT record's TTL * Add support for configuring OwnershipProcessor TXT record's TTL
* Add Plan.meta to allow providers to indicate they need to make changes to the
zone that are not record specific
## v1.10.0 - 2024-10-06 - Lots of little stuff ## v1.10.0 - 2024-10-06 - Lots of little stuff


+ 16
- 3
octodns/provider/base.py View File

@ -214,6 +214,14 @@ class BaseProvider(BaseSource):
''' '''
return [] return []
def _plan_meta(self, existing, desired, changes):
'''
An opportunity for providers to indicate they have "meta" changes
to the zone which are unrelated to records. Examples may include servive
plan changes, replication settings, and notes.
'''
return None
def supports_warn_or_except(self, msg, fallback): def supports_warn_or_except(self, msg, fallback):
if self.strict_supports: if self.strict_supports:
raise SupportsException(f'{self.id}: {msg}') raise SupportsException(f'{self.id}: {msg}')
@ -269,14 +277,19 @@ class BaseProvider(BaseSource):
) )
changes += extra changes += extra
if changes:
meta = self._plan_meta(
existing=existing, desired=desired, changes=changes
)
if changes or meta:
plan = Plan( plan = Plan(
existing, existing,
desired, desired,
changes, changes,
exists, exists,
self.update_pcent_threshold,
self.delete_pcent_threshold,
update_pcent_threshold=self.update_pcent_threshold,
delete_pcent_threshold=self.delete_pcent_threshold,
meta=meta,
) )
self.log.info('plan: %s', plan) self.log.info('plan: %s', plan)
return plan return plan


+ 27
- 22
octodns/provider/plan.py View File

@ -6,6 +6,7 @@ from collections import defaultdict
from io import StringIO from io import StringIO
from json import dumps from json import dumps
from logging import DEBUG, ERROR, INFO, WARN, getLogger from logging import DEBUG, ERROR, INFO, WARN, getLogger
from pprint import pformat
from sys import stdout from sys import stdout
@ -50,6 +51,7 @@ class Plan(object):
exists, exists,
update_pcent_threshold=MAX_SAFE_UPDATE_PCENT, update_pcent_threshold=MAX_SAFE_UPDATE_PCENT,
delete_pcent_threshold=MAX_SAFE_DELETE_PCENT, delete_pcent_threshold=MAX_SAFE_DELETE_PCENT,
meta=None,
): ):
self.existing = existing self.existing = existing
self.desired = desired self.desired = desired
@ -59,6 +61,7 @@ class Plan(object):
# them and/or is as safe as possible. # them and/or is as safe as possible.
self.changes = sorted(changes) self.changes = sorted(changes)
self.exists = exists self.exists = exists
self.meta = meta
# Zone thresholds take precedence over provider # Zone thresholds take precedence over provider
if existing and existing.update_pcent_threshold is not None: if existing and existing.update_pcent_threshold is not None:
@ -74,22 +77,11 @@ class Plan(object):
change_counts[change.__class__.__name__] += 1 change_counts[change.__class__.__name__] += 1
self.change_counts = change_counts self.change_counts = change_counts
try:
existing_n = len(self.existing.records)
except AttributeError:
existing_n = 0
self.log.debug(
'__init__: Creates=%d, Updates=%d, Deletes=%d Existing=%d',
self.change_counts['Create'],
self.change_counts['Update'],
self.change_counts['Delete'],
existing_n,
)
self.log.debug('__init__: %s', self.__repr__())
@property @property
def data(self): def data(self):
return {'changes': [c.data for c in self.changes]}
return {'changes': [c.data for c in self.changes], 'meta': self.meta}
def raise_if_unsafe(self): def raise_if_unsafe(self):
if ( if (
@ -140,11 +132,12 @@ class Plan(object):
creates = self.change_counts['Create'] creates = self.change_counts['Create']
updates = self.change_counts['Update'] updates = self.change_counts['Update']
deletes = self.change_counts['Delete'] deletes = self.change_counts['Delete']
existing = len(self.existing.records)
return (
f'Creates={creates}, Updates={updates}, Deletes={deletes}, '
f'Existing Records={existing}'
)
try:
existing = len(self.existing.records)
except AttributeError:
existing = 0
meta = self.meta is not None
return f'Creates={creates}, Updates={updates}, Deletes={deletes}, Existing={existing}, Meta={meta}'
class _PlanOutput(object): class _PlanOutput(object):
@ -167,10 +160,7 @@ class PlanLogger(_PlanOutput):
raise Exception(f'Unsupported level: {level}') raise Exception(f'Unsupported level: {level}')
def run(self, log, plans, *args, **kwargs): def run(self, log, plans, *args, **kwargs):
hr = (
'*************************************************************'
'*******************\n'
)
hr = '********************************************************************************\n'
buf = StringIO() buf = StringIO()
buf.write('\n') buf.write('\n')
if plans: if plans:
@ -199,6 +189,11 @@ class PlanLogger(_PlanOutput):
buf.write(change.__repr__(leader='* ')) buf.write(change.__repr__(leader='* '))
buf.write('\n* ') buf.write('\n* ')
if plan.meta:
buf.write('Meta: \n')
buf.write(pformat(plan.meta, indent=2, sort_dicts=True))
buf.write('\n')
buf.write('Summary: ') buf.write('Summary: ')
buf.write(str(plan)) buf.write(str(plan))
buf.write('\n') buf.write('\n')
@ -291,6 +286,11 @@ class PlanMarkdown(_PlanOutput):
fh.write(new.source.id) fh.write(new.source.id)
fh.write(' |\n') fh.write(' |\n')
if plan.meta:
fh.write('\nMeta: ')
fh.write(pformat(plan.meta, indent=2, sort_dicts=True))
fh.write('\n')
fh.write('\nSummary: ') fh.write('\nSummary: ')
fh.write(str(plan)) fh.write(str(plan))
fh.write('\n\n') fh.write('\n\n')
@ -361,6 +361,11 @@ class PlanHtml(_PlanOutput):
fh.write(new.source.id) fh.write(new.source.id)
fh.write('</td>\n </tr>\n') fh.write('</td>\n </tr>\n')
if plan.meta:
fh.write(' <tr>\n <td colspan=6>Meta: ')
fh.write(pformat(plan.meta, indent=2, sort_dicts=True))
fh.write('</td>\n </tr>\n</table>\n')
fh.write(' <tr>\n <td colspan=6>Summary: ') fh.write(' <tr>\n <td colspan=6>Summary: ')
fh.write(str(plan)) fh.write(str(plan))
fh.write('</td>\n </tr>\n</table>\n') fh.write('</td>\n </tr>\n</table>\n')


+ 6
- 5
tests/test_octodns_plan.py View File

@ -64,6 +64,7 @@ changes = [create, create2, delete, update]
plans = [ plans = [
(simple, Plan(zone, zone, changes, True)), (simple, Plan(zone, zone, changes, True)),
(simple, Plan(zone, zone, changes, False)), (simple, Plan(zone, zone, changes, False)),
(simple, Plan(zone, zone, changes, False, meta={'key': 'val'})),
] ]
@ -105,8 +106,8 @@ class TestPlanLogger(TestCase):
PlanLogger('logger').run(log, plans) PlanLogger('logger').run(log, plans)
out = log.out.getvalue() out = log.out.getvalue()
self.assertTrue( self.assertTrue(
'Summary: Creates=2, Updates=1, '
'Deletes=1, Existing Records=0' in out
'Summary: Creates=2, Updates=1, Deletes=1, Existing=0, Meta=False'
in out
) )
@ -123,8 +124,8 @@ class TestPlanHtml(TestCase):
PlanHtml('html').run(plans, fh=out) PlanHtml('html').run(plans, fh=out)
out = out.getvalue() out = out.getvalue()
self.assertTrue( self.assertTrue(
' <td colspan=6>Summary: Creates=2, Updates=1, '
'Deletes=1, Existing Records=0</td>' in out
' <td colspan=6>Summary: Creates=2, Updates=1, Deletes=1, Existing=0, Meta=False</td>'
in out
) )
@ -398,7 +399,7 @@ class TestPlanSafety(TestCase):
def test_data(self): def test_data(self):
data = plans[0][1].data data = plans[0][1].data
# plans should have a single key, changes # plans should have a single key, changes
self.assertEqual(('changes',), tuple(data.keys()))
self.assertEqual(('changes', 'meta'), tuple(data.keys()))
# it should be a list # it should be a list
self.assertIsInstance(data['changes'], list) self.assertIsInstance(data['changes'], list)
# w/4 elements # w/4 elements


Loading…
Cancel
Save