def test_simple(self): with TemporaryDirectory() as tmpdir: environ['YAML_TMP_DIR'] = tmpdir.dirname tc = Manager(get_config_filename('simple.yaml')) \ .sync(dry_run=False) self.assertEquals(22, tc) # try with just one of the zones tc = Manager(get_config_filename('simple.yaml')) \ .sync(dry_run=False, eligible_zones=['unit.tests.']) self.assertEquals(16, tc) # the subzone, with 2 targets tc = Manager(get_config_filename('simple.yaml')) \ .sync(dry_run=False, eligible_zones=['subzone.unit.tests.']) self.assertEquals(6, tc) # and finally the empty zone tc = Manager(get_config_filename('simple.yaml')) \ .sync(dry_run=False, eligible_zones=['empty.']) self.assertEquals(0, tc) # Again with force tc = Manager(get_config_filename('simple.yaml')) \ .sync(dry_run=False, force=True) self.assertEquals(22, tc) # Again with max_workers = 1 tc = Manager(get_config_filename('simple.yaml'), max_workers=1) \ .sync(dry_run=False, force=True) self.assertEquals(22, tc) # Include meta tc = Manager(get_config_filename('simple.yaml'), max_workers=1, include_meta=True) \ .sync(dry_run=False, force=True) self.assertEquals(26, tc)
def test_aliases(self): with TemporaryDirectory() as tmpdir: environ['YAML_TMP_DIR'] = tmpdir.dirname # Alias zones with a valid target. tc = Manager(get_config_filename('simple-alias-zone.yaml')) \ .sync() self.assertEquals(0, tc) # Alias zone with an invalid target. with self.assertRaises(ManagerException) as ctx: tc = Manager(get_config_filename('unknown-source-zone.yaml')) \ .sync() self.assertEquals( 'Invalid alias zone alias.tests.: source zone ' 'does-not-exists.tests. does not exist', text_type(ctx.exception)) # Alias zone that points to another alias zone. with self.assertRaises(ManagerException) as ctx: tc = Manager(get_config_filename('alias-zone-loop.yaml')) \ .sync() self.assertEquals( 'Invalid alias zone alias-loop.tests.: source ' 'zone alias.tests. is an alias zone', text_type(ctx.exception))
def test_populate_lenient_fallback(self): with TemporaryDirectory() as tmpdir: environ['YAML_TMP_DIR'] = tmpdir.dirname # Only allow a target that doesn't exist manager = Manager(get_config_filename('simple.yaml')) class NoLenient(SimpleProvider): def populate(self, zone): pass # This should be ok, we'll fall back to not passing it manager._populate_and_plan('unit.tests.', [], [NoLenient()], []) class OtherType(SimpleProvider): def populate(self, zone, lenient=False): raise TypeError('something else') # This will blow up, we don't fallback for source with self.assertRaises(TypeError) as ctx: manager._populate_and_plan('unit.tests.', [], [OtherType()], []) self.assertEquals('something else', text_type(ctx.exception))
def test_provider(self): source = YamlProvider('test', join(dirname(__file__), 'config')) zone = Zone('unit.tests.', []) # With target we don't add anything source.populate(zone, target=source) self.assertEquals(0, len(zone.records)) # without it we see everything source.populate(zone) self.assertEquals(18, len(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 # pulled in yet again and still match up. That assumes that the input # data completely exercises things. This assumption can be tested by # relatively well by running # ./script/coverage tests/test_octodns_provider_yaml.py and # looking at the coverage file # ./htmlcov/octodns_provider_yaml_py.html with TemporaryDirectory() as td: # Add some subdirs to make sure that it can create them directory = join(td.dirname, 'sub', 'dir') yaml_file = join(directory, 'unit.tests.yaml') target = YamlProvider('test', directory) # We add everything plan = target.plan(zone) self.assertEquals( 14, len(filter(lambda c: isinstance(c, Create), plan.changes))) self.assertFalse(isfile(yaml_file)) # Now actually do it self.assertEquals(14, target.apply(plan)) self.assertTrue(isfile(yaml_file)) # There should be no changes after the round trip reloaded = Zone('unit.tests.', []) target.populate(reloaded) self.assertFalse(zone.changes(reloaded, target=source)) # A 2nd sync should still create everything plan = target.plan(zone) self.assertEquals( 14, len(filter(lambda c: isinstance(c, Create), plan.changes))) with open(yaml_file) as fh: data = safe_load(fh.read()) # these are stored as plural 'values' for r in data['']: self.assertTrue('values' in r) self.assertTrue('values' in data['mx']) self.assertTrue('values' in data['naptr']) self.assertTrue('values' in data['_srv._tcp']) self.assertTrue('values' in data['txt']) # these are stored as singular 'value' self.assertTrue('value' in data['aaaa']) self.assertTrue('value' in data['ptr']) self.assertTrue('value' in data['spf']) self.assertTrue('value' in data['www'])
def test_provider(self): source = YamlProvider('test', join(dirname(__file__), 'config')) zone = Zone('unit.tests.', []) dynamic_zone = Zone('dynamic.tests.', []) # With target we don't add anything source.populate(zone, target=source) self.assertEqual(0, len(zone.records)) # without it we see everything source.populate(zone) self.assertEqual(25, len(zone.records)) source.populate(dynamic_zone) self.assertEqual(6, 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 # pulled in yet again and still match up. That assumes that the input # data completely exercises things. This assumption can be tested by # relatively well by running # ./script/coverage tests/test_octodns_provider_yaml.py and # looking at the coverage file # ./htmlcov/octodns_provider_yaml_py.html with TemporaryDirectory() as td: # Add some subdirs to make sure that it can create them directory = join(td.dirname, 'sub', 'dir') yaml_file = join(directory, 'unit.tests.yaml') dynamic_yaml_file = join(directory, 'dynamic.tests.yaml') target = YamlProvider('test', directory, supports_root_ns=False) # We add everything plan = target.plan(zone) self.assertEqual( 22, len([c for c in plan.changes if isinstance(c, Create)])) self.assertFalse(isfile(yaml_file)) # Now actually do it self.assertEqual(22, target.apply(plan)) self.assertTrue(isfile(yaml_file)) # Dynamic plan plan = target.plan(dynamic_zone) self.assertEqual( 6, len([c for c in plan.changes if isinstance(c, Create)])) self.assertFalse(isfile(dynamic_yaml_file)) # Apply it self.assertEqual(6, target.apply(plan)) self.assertTrue(isfile(dynamic_yaml_file)) # There should be no changes after the round trip reloaded = Zone('unit.tests.', []) target.populate(reloaded) self.assertDictEqual( {'included': ['test']}, [x for x in reloaded.records if x.name == 'included'][0]._octodns, ) # manually copy over the root since it will have been ignored # when things were written out reloaded.add_record(zone.root_ns) self.assertFalse(zone.changes(reloaded, target=source)) # A 2nd sync should still create everything plan = target.plan(zone) self.assertEqual( 22, len([c for c in plan.changes if isinstance(c, Create)])) with open(yaml_file) as fh: data = safe_load(fh.read()) # '' has some of both roots = sorted(data.pop(''), key=lambda r: r['type']) self.assertTrue('values' in roots[0]) # A self.assertTrue('geo' in roots[0]) # geo made the trip self.assertTrue('value' in roots[1]) # CAA self.assertTrue('values' in roots[2]) # SSHFP # these are stored as plural 'values' self.assertTrue('values' in data.pop('_srv._tcp')) self.assertTrue('values' in data.pop('mx')) self.assertTrue('values' in data.pop('naptr')) self.assertTrue('values' in data.pop('sub')) self.assertTrue('values' in data.pop('txt')) self.assertTrue('values' in data.pop('loc')) self.assertTrue('values' in data.pop('urlfwd')) self.assertTrue('values' in data.pop('sub.txt')) self.assertTrue('values' in data.pop('subzone')) # these are stored as singular 'value' self.assertTrue('value' in data.pop('_imap._tcp')) self.assertTrue('value' in data.pop('_pop3._tcp')) self.assertTrue('value' in data.pop('aaaa')) self.assertTrue('value' in data.pop('cname')) self.assertTrue('value' in data.pop('dname')) self.assertTrue('value' in data.pop('included')) self.assertTrue('value' in data.pop('ptr')) self.assertTrue('value' in data.pop('spf')) self.assertTrue('value' in data.pop('www')) self.assertTrue('value' in data.pop('www.sub')) # make sure nothing is left self.assertEqual([], list(data.keys())) with open(dynamic_yaml_file) as fh: data = safe_load(fh.read()) # make sure new dynamic records made the trip dyna = data.pop('a') self.assertTrue('values' in dyna) # self.assertTrue('dynamic' in dyna) # TODO: # make sure new dynamic records made the trip dyna = data.pop('aaaa') self.assertTrue('values' in dyna) # self.assertTrue('dynamic' in dyna) dyna = data.pop('cname') 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) dyna = data.pop('pool-only-in-fallback') self.assertTrue('value' in dyna) # self.assertTrue('dynamic' in dyna) # make sure nothing is left self.assertEqual([], list(data.keys()))
def test_provider(self): source = SplitYamlProvider('test', join(dirname(__file__), 'config/split'), extension='.tst') zone = Zone('unit.tests.', []) dynamic_zone = Zone('dynamic.tests.', []) # With target we don't add anything source.populate(zone, target=source) self.assertEqual(0, len(zone.records)) # without it we see everything source.populate(zone) self.assertEqual(20, len(zone.records)) source.populate(dynamic_zone) self.assertEqual(5, len(dynamic_zone.records)) with TemporaryDirectory() as td: # Add some subdirs to make sure that it can create them directory = join(td.dirname, 'sub', 'dir') zone_dir = join(directory, 'unit.tests.tst') dynamic_zone_dir = join(directory, 'dynamic.tests.tst') target = SplitYamlProvider('test', directory, extension='.tst', supports_root_ns=False) # We add everything plan = target.plan(zone) self.assertEqual( 17, len([c for c in plan.changes if isinstance(c, Create)])) self.assertFalse(isdir(zone_dir)) # Now actually do it self.assertEqual(17, target.apply(plan)) # Dynamic plan plan = target.plan(dynamic_zone) self.assertEqual( 5, len([c for c in plan.changes if isinstance(c, Create)])) self.assertFalse(isdir(dynamic_zone_dir)) # Apply it self.assertEqual(5, target.apply(plan)) self.assertTrue(isdir(dynamic_zone_dir)) # There should be no changes after the round trip reloaded = Zone('unit.tests.', []) target.populate(reloaded) self.assertDictEqual( {'included': ['test']}, [x for x in reloaded.records if x.name == 'included'][0]._octodns, ) # manually copy over the root since it will have been ignored # when things were written out reloaded.add_record(zone.root_ns) self.assertFalse(zone.changes(reloaded, target=source)) # A 2nd sync should still create everything plan = target.plan(zone) self.assertEqual( 17, len([c for c in plan.changes if isinstance(c, Create)])) yaml_file = join(zone_dir, '$unit.tests.yaml') self.assertTrue(isfile(yaml_file)) with open(yaml_file) as fh: data = safe_load(fh.read()) roots = sorted(data.pop(''), key=lambda r: r['type']) self.assertTrue('values' in roots[0]) # A self.assertTrue('geo' in roots[0]) # geo made the trip self.assertTrue('value' in roots[1]) # CAA self.assertTrue('values' in roots[2]) # SSHFP # These records are stored as plural "values." Check each file to # ensure correctness. for record_name in ( '_srv._tcp', 'mx', 'naptr', 'sub', 'txt', 'urlfwd', ): yaml_file = join(zone_dir, f'{record_name}.yaml') self.assertTrue(isfile(yaml_file)) with open(yaml_file) as fh: data = safe_load(fh.read()) self.assertTrue('values' in data.pop(record_name)) # These are stored as singular "value." Again, check each file. for record_name in ( 'aaaa', 'cname', 'dname', 'included', 'ptr', 'spf', 'www.sub', 'www', ): yaml_file = join(zone_dir, f'{record_name}.yaml') self.assertTrue(isfile(yaml_file)) with open(yaml_file) as fh: data = safe_load(fh.read()) self.assertTrue('value' in data.pop(record_name)) # Again with the plural, this time checking dynamic.tests. for record_name in ('a', 'aaaa', 'real-ish-a'): yaml_file = join(dynamic_zone_dir, f'{record_name}.yaml') self.assertTrue(isfile(yaml_file)) with open(yaml_file) as fh: data = safe_load(fh.read()) dyna = data.pop(record_name) self.assertTrue('values' in dyna) self.assertTrue('dynamic' in dyna) # Singular again. for record_name in ('cname', 'simple-weighted'): yaml_file = join(dynamic_zone_dir, f'{record_name}.yaml') self.assertTrue(isfile(yaml_file)) with open(yaml_file) as fh: data = safe_load(fh.read()) dyna = data.pop(record_name) self.assertTrue('value' in dyna) self.assertTrue('dynamic' in dyna)
def test_dump_output_provider(self): with TemporaryDirectory() as tmpdir: environ['YAML_TMP_DIR'] = tmpdir.dirname # this time we'll use seperate tmp dirs with TemporaryDirectory() as tmpdir2: environ['YAML_TMP_DIR2'] = tmpdir2.dirname manager = Manager(get_config_filename('simple.yaml')) # we're going to tell it to use dump2 to do the dumping, but a # copy should be made and directory set to tmpdir.dirname # rather than 2's tmpdir2.dirname manager.dump( zone='unit.tests.', output_dir=tmpdir.dirname, output_provider='dump2', sources=['in'], ) self.assertTrue(isfile(join(tmpdir.dirname, 'unit.tests.yaml'))) self.assertFalse( isfile(join(tmpdir2.dirname, 'unit.tests.yaml'))) # let's run that again, this time telling it to use tmpdir2 and # dump2 which should allow it to skip the copying manager.dump( zone='unit.tests.', output_dir=tmpdir2.dirname, output_provider='dump2', sources=['in'], ) self.assertTrue( isfile(join(tmpdir2.dirname, 'unit.tests.yaml'))) # tell it to use an output_provider that doesn't exist with self.assertRaises(ManagerException) as ctx: manager.dump( zone='unit.tests.', output_dir=tmpdir.dirname, output_provider='nope', sources=['in'], ) self.assertEqual('Unknown output_provider: nope', str(ctx.exception)) # tell it to use an output_provider that doesn't support # directory with self.assertRaises(ManagerException) as ctx: manager.dump( zone='unit.tests.', output_dir=tmpdir.dirname, output_provider='simple', sources=['in'], ) self.assertEqual( 'output_provider=simple, does not support ' 'directory property', str(ctx.exception), ) # hack a directory property onto the simple provider so that # it'll pass that check and fail the copy one instead manager.providers['simple'].directory = 42 with self.assertRaises(ManagerException) as ctx: manager.dump( zone='unit.tests.', output_dir=tmpdir.dirname, output_provider='simple', sources=['in'], ) self.assertEqual( 'output_provider=simple, does not support ' 'copy method', str(ctx.exception), )
def test_provider(self): source = EtcHostsProvider('test', path.join(dirname(__file__), 'config')) zone = Zone('unit.tests.', []) # We never populate anything, when acting as a source source.populate(zone, target=source) self.assertEquals(0, len(zone.records)) # Same if we're acting as a target source.populate(zone) self.assertEquals(0, len(zone.records)) record = Record.new(zone, '', { 'ttl': 60, 'type': 'ALIAS', 'value': 'www.unit.tests.' }) zone.add_record(record) record = Record.new(zone, 'www', { 'ttl': 60, 'type': 'AAAA', 'value': '2001:4860:4860::8888', }) zone.add_record(record) record = Record.new(zone, 'www', { 'ttl': 60, 'type': 'A', 'values': ['1.1.1.1', '2.2.2.2'], }) zone.add_record(record) record = record.new(zone, 'v6', { 'ttl': 60, 'type': 'AAAA', 'value': '2001:4860:4860::8844', }) zone.add_record(record) record = record.new(zone, 'start', { 'ttl': 60, 'type': 'CNAME', 'value': 'middle.unit.tests.', }) zone.add_record(record) record = record.new(zone, 'middle', { 'ttl': 60, 'type': 'CNAME', 'value': 'unit.tests.', }) zone.add_record(record) record = record.new(zone, 'ext', { 'ttl': 60, 'type': 'CNAME', 'value': 'github.com.', }) zone.add_record(record) record = record.new(zone, '*', { 'ttl': 60, 'type': 'A', 'value': '3.3.3.3', }) zone.add_record(record) with TemporaryDirectory() as td: # Add some subdirs to make sure that it can create them directory = path.join(td.dirname, 'sub', 'dir') hosts_file = path.join(directory, 'unit.tests.hosts') target = EtcHostsProvider('test', directory) # We add everything plan = target.plan(zone) self.assertEquals(len(zone.records), len(plan.changes)) self.assertFalse(isfile(hosts_file)) # Now actually do it self.assertEquals(len(zone.records), target.apply(plan)) self.assertTrue(isfile(hosts_file)) with open(hosts_file) as fh: data = fh.read() # v6 self.assertTrue('2001:4860:4860::8844\tv6.unit.tests' in data) # www self.assertTrue('1.1.1.1\twww.unit.tests' in data) # root ALIAS self.assertTrue('# unit.tests -> www.unit.tests' in data) self.assertTrue('1.1.1.1\tunit.tests' in data) self.assertTrue( '# start.unit.tests -> middle.unit.tests' in data) self.assertTrue('# middle.unit.tests -> unit.tests' in data) self.assertTrue('# unit.tests -> www.unit.tests' in data) self.assertTrue('1.1.1.1 start.unit.tests' in data) # second empty run that won't create dirs and overwrites file plan = Plan(zone, zone, [], True) self.assertEquals(0, target.apply(plan))