Example #1
0
 def test_missing_env_config(self):
     with self.assertRaises(Exception) as ctx:
         Manager(get_config_filename('missing-provider-env.yaml')).sync()
     self.assertTrue('missing env var' in ctx.exception.message)
Example #2
0
 def test_bad_provider_class_no_module(self):
     with self.assertRaises(Exception) as ctx:
         Manager(get_config_filename('bad-provider-class-no-module.yaml')) \
             .sync()
     self.assertTrue('Unknown provider class' in ctx.exception.message)
Example #3
0
 def test_missing_provider_config(self):
     # Missing provider config
     with self.assertRaises(Exception) as ctx:
         Manager(get_config_filename('missing-provider-config.yaml')).sync()
     self.assertTrue('provider config' in ctx.exception.message)
 def test_source_only_as_a_target(self):
     with self.assertRaises(ManagerException) as ctx:
         Manager(get_config_filename('provider-problems.yaml')) \
             .sync(['not.targetable.'])
     self.assertTrue(
         'does not support targeting' in text_type(ctx.exception))
Example #5
0
def main():
    parser = ArgumentParser(description=__doc__.split('\n')[1])

    parser.add_argument('--config-file',
                        required=True,
                        help='The Manager configuration file to use')
    parser.add_argument('--zone', required=True, help='Zone to dump')
    parser.add_argument('--source',
                        required=True,
                        default=[],
                        action='append',
                        help='Source(s) to pull data from')
    parser.add_argument('--num-workers',
                        default=4,
                        help='Number of background workers')
    parser.add_argument('--timeout',
                        default=1,
                        help='Number seconds to wait for an answer')
    parser.add_argument('server', nargs='+', help='Servers to query')

    args = parser.parse_args()

    manager = Manager(args.config_file)

    log = getLogger('report')

    try:
        sources = [manager.providers[source] for source in args.source]
    except KeyError as e:
        raise Exception('Unknown source: {}'.format(e.args[0]))

    zone = Zone(args.zone, manager.configured_sub_zones(args.zone))
    for source in sources:
        source.populate(zone)

    print('name,type,ttl,{},consistent'.format(','.join(args.server)))
    resolvers = []
    ip_addr_re = re.compile(r'^[\d\.]+$')
    for server in args.server:
        resolver = AsyncResolver(configure=False,
                                 num_workers=int(args.num_workers))
        if not ip_addr_re.match(server):
            server = str(query(server, 'A')[0])
        log.info('server=%s', server)
        resolver.nameservers = [server]
        resolver.lifetime = int(args.timeout)
        resolvers.append(resolver)

    queries = {}
    for record in sorted(zone.records):
        queries[record] = [
            r.query(record.fqdn, record._type) for r in resolvers
        ]

    for record, futures in sorted(queries.items(), key=lambda d: d[0]):
        stdout.write(record.fqdn)
        stdout.write(',')
        stdout.write(record._type)
        stdout.write(',')
        stdout.write(str(record.ttl))
        compare = {}
        for future in futures:
            stdout.write(',')
            try:
                answers = [str(r) for r in future.result()]
            except (NoAnswer, NoNameservers):
                answers = ['*no answer*']
            except NXDOMAIN:
                answers = ['*does not exist*']
            except Timeout:
                answers = ['*timeout*']
            stdout.write(' '.join(answers))
            # sorting to ignore order
            answers = '*:*'.join(sorted(answers)).lower()
            compare[answers] = True
        stdout.write(',True\n' if len(compare) == 1 else ',False\n')
Example #6
0
 def test_source_only_as_a_target(self):
     with self.assertRaises(Exception) as ctx:
         Manager(get_config_filename('unknown-provider.yaml')) \
             .sync(['not.targetable.'])
     self.assertTrue('does not support targeting' in ctx.exception.message)
Example #7
0
    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_missing_targets(self):
     with self.assertRaises(ManagerException) as ctx:
         Manager(get_config_filename('provider-problems.yaml')) \
             .sync(['missing.targets.'])
     self.assertTrue('missing targets' in text_type(ctx.exception))
 def test_unknown_target(self):
     with self.assertRaises(ManagerException) as ctx:
         Manager(get_config_filename('provider-problems.yaml')) \
             .sync(['unknown.target.'])
     self.assertTrue('unknown target' in text_type(ctx.exception))
 def test_missing_provider_class(self):
     with self.assertRaises(ManagerException) as ctx:
         Manager(get_config_filename('missing-provider-class.yaml')).sync()
     self.assertTrue('missing class' in text_type(ctx.exception))
 def test_bad_provider_class_module(self):
     with self.assertRaises(ManagerException) as ctx:
         Manager(get_config_filename('bad-provider-class-module.yaml')) \
             .sync()
     self.assertTrue('Unknown provider class' in text_type(ctx.exception))
Example #12
0
 def test_unknown_source(self):
     with self.assertRaises(ManagerException) as ctx:
         Manager(get_config_filename('provider-problems.yaml')).sync(
             ['unknown.source.'])
     self.assertTrue('unknown source' in str(ctx.exception))
Example #13
0
 def test_missing_source(self):
     with self.assertRaises(ManagerException) as ctx:
         Manager(get_config_filename('provider-problems.yaml')).sync(
             ['missing.sources.'])
     self.assertTrue('missing sources' in str(ctx.exception))
Example #14
0
 def test_bad_provider_class(self):
     with self.assertRaises(ManagerException) as ctx:
         Manager(get_config_filename('bad-provider-class.yaml')).sync()
     self.assertTrue('Unknown provider class' in str(ctx.exception))
Example #15
0
 def test_missing_targets(self):
     with self.assertRaises(Exception) as ctx:
         Manager(get_config_filename('unknown-provider.yaml')) \
             .sync(['missing.targets.'])
     self.assertTrue('missing targets' in ctx.exception.message)
 def test_bad_plan_output_class(self):
     with self.assertRaises(ManagerException) as ctx:
         name = 'bad-plan-output-missing-class.yaml'
         Manager(get_config_filename(name)).sync()
     self.assertEquals('plan_output bad is missing class',
                       text_type(ctx.exception))
Example #17
0
 def test_unknown_target(self):
     with self.assertRaises(Exception) as ctx:
         Manager(get_config_filename('unknown-provider.yaml')) \
             .sync(['unknown.target.'])
     self.assertTrue('unknown target' in ctx.exception.message)
 def test_bad_plan_output_config(self):
     with self.assertRaises(ManagerException) as ctx:
         Manager(get_config_filename('bad-plan-output-config.yaml')).sync()
     self.assertEqual('Incorrect plan_output config for bad',
                      text_type(ctx.exception))
Example #19
0
def main():
    """check-zone based on octodns config file and dns zone
    Will query all 4 DNS servers configured for the zone in GCP.
    """
    parser = ArgumentParser(description=__doc__.split('\n')[1])

    parser.add_argument('--config-file',
                        required=True,
                        help='The OctoDNS configuration file to use')
    parser.add_argument('--zone',
                        action='append',
                        required=True,
                        help='zone to check')

    args = parser.parse_args()

    manager = Manager(args.config_file)

    for zone_name in args.zone:
        print('Checking records for {}'.format(zone_name))
        zone = Zone(zone_name, manager.configured_sub_zones(zone_name))

        # Read our YAML configuration
        yaml_config = manager.providers['config']

        # Build a GCP provider in our project to read the nameservers from it
        gcp = manager.providers['gcp']
        project = gcp.gcloud_client.project

        # Retrieve the DNS Servers directly from the GCP configuration
        dns_servers = gcp.gcloud_zones[zone_name].name_servers

        # k8s.io resolvers for testing without access to gcp
        #dns_servers = ["NS-CLOUD-D1.GOOGLEDOMAINS.COM", "NS-CLOUD-D2.GOOGLEDOMAINS.COM", "NS-CLOUD-D3.GOOGLEDOMAINS.COM", "NS-CLOUD-D4.GOOGLEDOMAINS.COM"]
        print('Using GCP project {}'.format(project))
        print('name,type,ttl,{},consistent'.format(','.join(dns_servers)))

        # Populate the zone with those records defined in our YAML config
        yaml_config.populate(zone)

        # This would populate the zone with records already defined in Google Cloud DNS
        # gcp.populate(zone)

        # Configure Resolvers (one per DNS server)
        resolvers = configure_resolvers(dns_servers)

        # Populate the queries to make based on zone record configuration
        queries = {}
        for record in sorted(zone.records):
            queries[record] = [
                r.query(record.fqdn, record._type) for r in resolvers
            ]
        # No dns_error unless we find one
        dns_error = False

        dns_error = verify_dns(queries)

        if dns_error:
            sys.exit(1)

    sys.exit(0)
Example #20
0
    def test_processors(self):
        manager = Manager(get_config_filename('simple.yaml'))

        targets = [PlannableProvider('prov')]

        zone = Zone('unit.tests.', [])
        record = Record.new(zone, 'a', {
            'ttl': 30,
            'type': 'A',
            'value': '1.2.3.4',
        })

        # muck with sources
        class MockProcessor(BaseProcessor):

            def process_source_zone(self, zone, sources):
                zone = self._clone_zone(zone)
                zone.add_record(record)
                return zone

        mock = MockProcessor('mock')
        plans, zone = manager._populate_and_plan('unit.tests.', [mock], [],
                                                 targets)
        # Our mock was called and added the record
        self.assertEquals(record, list(zone.records)[0])
        # We got a create for the thing added to the expected state (source)
        self.assertIsInstance(plans[0][1].changes[0], Create)

        # muck with targets
        class MockProcessor(BaseProcessor):

            def process_target_zone(self, zone, target):
                zone = self._clone_zone(zone)
                zone.add_record(record)
                return zone

        mock = MockProcessor('mock')
        plans, zone = manager._populate_and_plan('unit.tests.', [mock], [],
                                                 targets)
        # No record added since it's target this time
        self.assertFalse(zone.records)
        # We got a delete for the thing added to the existing state (target)
        self.assertIsInstance(plans[0][1].changes[0], Delete)

        # muck with plans
        class MockProcessor(BaseProcessor):

            def process_target_zone(self, zone, target):
                zone = self._clone_zone(zone)
                zone.add_record(record)
                return zone

            def process_plan(self, plans, sources, target):
                # get rid of the change
                plans.changes.pop(0)

        mock = MockProcessor('mock')
        plans, zone = manager._populate_and_plan('unit.tests.', [mock], [],
                                                 targets)
        # We planned a delete again, but this time removed it from the plan, so
        # no plans
        self.assertFalse(plans)