From 17c294042f570a6cdc89f8f25fa13a24bc0d4a5d Mon Sep 17 00:00:00 2001 From: Ross McFarland Date: Thu, 14 Apr 2022 10:19:33 -0700 Subject: [PATCH] Implement a sketch of --output-provider support for dump --- octodns/cmds/dump.py | 8 +++++-- octodns/manager.py | 40 ++++++++++++++++++++++++++--------- octodns/provider/yaml.py | 6 ++++++ tests/test_octodns_manager.py | 25 ++++++++++++---------- 4 files changed, 56 insertions(+), 23 deletions(-) diff --git a/octodns/cmds/dump.py b/octodns/cmds/dump.py index 9927468..c3eaad2 100755 --- a/octodns/cmds/dump.py +++ b/octodns/cmds/dump.py @@ -18,6 +18,9 @@ def main(): parser.add_argument('--output-dir', required=True, help='The directory into which the results will be ' 'written (Note: will overwrite existing files)') + parser.add_argument('--output-provider', required=False, + help='The configured provider to use when dumping ' + 'records. Must support copy() and directory') parser.add_argument('--lenient', action='store_true', default=False, help='Ignore record validations and do a best effort ' 'dump') @@ -31,8 +34,9 @@ def main(): args = parser.parse_args() manager = Manager(args.config_file) - manager.dump(args.zone, args.output_dir, args.lenient, args.split, - *args.source) + manager.dump(zone=args.zone, output_dir=args.output_dir, + output_provider=args.output_provider, lenient=args.lenient, + split=args.split, sources=args.source) if __name__ == '__main__': diff --git a/octodns/manager.py b/octodns/manager.py index e640a8d..03549b0 100644 --- a/octodns/manager.py +++ b/octodns/manager.py @@ -549,25 +549,45 @@ class Manager(object): return zb.changes(za, _AggregateTarget(a + b)) - def dump(self, zone, output_dir, lenient, split, source, *sources): + def dump(self, zone, output_dir, sources, lenient=False, split=False, + output_provider=None): ''' Dump zone data from the specified source ''' - self.log.info('dump: zone=%s, sources=%s', zone, sources) - - # We broke out source to force at least one to be passed, add it to any - # others we got. - sources = [source] + list(sources) + self.log.info('dump: zone=%s, output_dir=%s, output_provider=%s, ' + 'lenient=%s, split=%s, sources=%s', zone, output_dir, + output_provider, lenient, split, sources) try: sources = [self.providers[s] for s in sources] except KeyError as e: raise ManagerException(f'Unknown source: {e.args[0]}') - clz = YamlProvider - if split: - clz = SplitYamlProvider - target = clz('dump', output_dir) + if output_provider: + self.log.info('dump: using specified output_provider=%s', + output_provider) + try: + target = self.providers[output_provider] + except KeyError as e: + raise ManagerException(f'Unknown output_provider: {e.args[0]}') + if not hasattr(target, 'directory'): + msg = f'output_provider={output_provider}, does not support ' \ + 'directory' + raise ManagerException(msg) + if not hasattr(target, 'copy'): + msg = f'output_provider={output_provider}, does not support ' \ + 'copy' + raise ManagerException(msg) + target = target.copy() + self.log.info('dump: setting directory of output_provider copy to ' + '%s', output_dir) + target.directory = output_dir + else: + self.log.info('dump: using custom YamlProvider') + clz = YamlProvider + if split: + clz = SplitYamlProvider + target = clz('dump', output_dir) zone = Zone(zone, self.configured_sub_zones(zone)) for source in sources: diff --git a/octodns/provider/yaml.py b/octodns/provider/yaml.py index 16a4bc1..8a37567 100644 --- a/octodns/provider/yaml.py +++ b/octodns/provider/yaml.py @@ -126,6 +126,12 @@ class YamlProvider(BaseProvider): self.populate_should_replace = populate_should_replace self.supports_root_ns = supports_root_ns + def copy(self): + args = dict(self.__dict__) + args['id'] = f'{args["id"]}-copy' + del args['log'] + return self.__class__(**args) + @property def SUPPORTS_ROOT_NS(self): return self.supports_root_ns diff --git a/tests/test_octodns_manager.py b/tests/test_octodns_manager.py index 6d85924..a96f265 100644 --- a/tests/test_octodns_manager.py +++ b/tests/test_octodns_manager.py @@ -283,24 +283,26 @@ class TestManager(TestCase): manager = Manager(get_config_filename('simple.yaml')) with self.assertRaises(ManagerException) as ctx: - manager.dump('unit.tests.', tmpdir.dirname, False, False, - 'nope') + manager.dump(zone='unit.tests.', output_dir=tmpdir.dirname, + sources=['nope']) self.assertEqual('Unknown source: nope', str(ctx.exception)) - manager.dump('unit.tests.', tmpdir.dirname, False, False, 'in') + manager.dump(zone='unit.tests.', output_dir=tmpdir.dirname, + sources=['in']) # make sure this fails with an IOError and not a KeyError when # tyring to find sub zones with self.assertRaises(IOError): - manager.dump('unknown.zone.', tmpdir.dirname, False, False, - 'in') + manager.dump(zone='unknown.zone.', output_dir=tmpdir.dirname, + sources=['in']) def test_dump_empty(self): with TemporaryDirectory() as tmpdir: environ['YAML_TMP_DIR'] = tmpdir.dirname manager = Manager(get_config_filename('simple.yaml')) - manager.dump('empty.', tmpdir.dirname, False, False, 'in') + manager.dump(zone='empty.', output_dir=tmpdir.dirname, + sources=['in']) with open(join(tmpdir.dirname, 'empty.yaml')) as fh: data = safe_load(fh, False) @@ -312,17 +314,18 @@ class TestManager(TestCase): manager = Manager(get_config_filename('simple-split.yaml')) with self.assertRaises(ManagerException) as ctx: - manager.dump('unit.tests.', tmpdir.dirname, False, True, - 'nope') + manager.dump(zone='unit.tests.', output_dir=tmpdir.dirname, + split=True, sources=['nope']) self.assertEqual('Unknown source: nope', str(ctx.exception)) - manager.dump('unit.tests.', tmpdir.dirname, False, True, 'in') + manager.dump(zone='unit.tests.', output_dir=tmpdir.dirname, + split=True, sources=['in']) # make sure this fails with an OSError and not a KeyError when # tyring to find sub zones with self.assertRaises(OSError): - manager.dump('unknown.zone.', tmpdir.dirname, False, True, - 'in') + manager.dump(zone='unknown.zone.', output_dir=tmpdir.dirname, + split=True, sources=['in']) def test_validate_configs(self): Manager(get_config_filename('simple-validate.yaml')).validate_configs()