1
0
mirror of https://github.com/github/octodns.git synced 2024-05-11 05:55:00 +00:00

Merge pull request #891 from octodns/log-version

Log the octoDNS version as part of Manager.__init__ info line
This commit is contained in:
Ross McFarland
2022-03-30 18:57:47 -07:00
committed by GitHub
5 changed files with 105 additions and 10 deletions

View File

@@ -16,6 +16,10 @@
* Record.register_type added so that providers can register custom record
types, see [docs/records.md](docs/records.md) for more information
#### Stuff
* Manager includes the octoDNS version in its init log line
## v0.9.16 - 2022-03-04 - Manage the root of the problem
#### Noteworthy changes

View File

@@ -11,6 +11,7 @@ from os import environ
from sys import stdout
import logging
from . import __VERSION__
from .provider.base import BaseProvider
from .provider.plan import Plan
from .provider.yaml import SplitYamlProvider, YamlProvider
@@ -19,6 +20,18 @@ from .yaml import safe_load
from .zone import Zone
# TODO: this can go away once we no longer support python 3.7
try:
from importlib.metadata import PackageNotFoundError, \
version as module_version
except ModuleNotFoundError: # pragma: no cover
class PackageNotFoundError(Exception):
pass
def module_version(*args, **kargs):
raise PackageNotFoundError('placeholder')
class _AggregateTarget(object):
id = 'aggregate'
@@ -84,7 +97,9 @@ class Manager(object):
return len(plan.changes[0].record.zone.name) if plan.changes else 0
def __init__(self, config_file, max_workers=None, include_meta=False):
self.log.info('__init__: config_file=%s', config_file)
version = self._try_version('octodns', version=__VERSION__)
self.log.info('__init__: config_file=%s (octoDNS %s)', config_file,
version)
# Read our config file
with open(config_file, 'r') as fh:
@@ -113,10 +128,12 @@ class Manager(object):
self.log.exception('Invalid provider class')
raise ManagerException(f'Provider {provider_name} is missing '
'class')
_class = self._get_named_class('provider', _class)
_class, module, version = self._get_named_class('provider', _class)
kwargs = self._build_kwargs(provider_config)
try:
self.providers[provider_name] = _class(provider_name, **kwargs)
self.log.info('__init__: provider=%s (%s %s)', provider_name,
module, version)
except TypeError:
self.log.exception('Invalid provider config')
raise ManagerException('Incorrect provider config for ' +
@@ -131,11 +148,14 @@ class Manager(object):
self.log.exception('Invalid processor class')
raise ManagerException(f'Processor {processor_name} is '
'missing class')
_class = self._get_named_class('processor', _class)
_class, module, version = self._get_named_class('processor',
_class)
kwargs = self._build_kwargs(processor_config)
try:
self.processors[processor_name] = _class(processor_name,
**kwargs)
self.log.info('__init__: processor=%s (%s %s)', processor_name,
module, version)
except TypeError:
self.log.exception('Invalid processor config')
raise ManagerException('Incorrect processor config for ' +
@@ -163,7 +183,7 @@ class Manager(object):
self.plan_outputs = {}
plan_outputs = manager_config.get('plan_outputs', {
'logger': {
'_logger': {
'class': 'octodns.provider.plan.PlanLogger',
'level': 'info'
}
@@ -175,26 +195,60 @@ class Manager(object):
self.log.exception('Invalid plan_output class')
raise ManagerException(f'plan_output {plan_output_name} is '
'missing class')
_class = self._get_named_class('plan_output', _class)
_class, module, version = self._get_named_class('plan_output',
_class)
kwargs = self._build_kwargs(plan_output_config)
try:
self.plan_outputs[plan_output_name] = \
_class(plan_output_name, **kwargs)
# Don't print out version info for the default output
if plan_output_name != '_logger':
self.log.info('__init__: plan_output=%s (%s %s)',
plan_output_name, module, version)
except TypeError:
self.log.exception('Invalid plan_output config')
raise ManagerException('Incorrect plan_output config for ' +
plan_output_name)
def _try_version(self, module_name, module=None, version=None):
try:
# Always try and use the official lookup first
return module_version(module_name)
except PackageNotFoundError:
pass
# If we were passed a version that's next in line
if version is not None:
return version
# finally try and import the module and see if it has a __VERSION__
if module is None:
module = import_module(module_name)
return getattr(module, '__VERSION__', None)
def _import_module(self, module_name):
current = module_name
_next = current.rsplit('.', 1)[0]
module = import_module(current)
version = self._try_version(current, module=module)
# If we didn't find a version in the specific module we're importing,
# we'll try walking up the hierarchy, as long as there is one (`.`),
# looking for it.
while version is None and current != _next:
current = _next
_next = current.rsplit('.', 1)[0]
version = self._try_version(current)
return module, version or 'n/a'
def _get_named_class(self, _type, _class):
try:
module_name, class_name = _class.rsplit('.', 1)
module = import_module(module_name)
module, version = self._import_module(module_name)
except (ImportError, ValueError):
self.log.exception('_get_{}_class: Unable to import '
'module %s', _class)
raise ManagerException(f'Unknown {_type} class: {_class}')
try:
return getattr(module, class_name)
return getattr(module, class_name), module_name, version
except AttributeError:
self.log.exception('_get_{}_class: Unable to get class %s '
'from module %s', class_name, module)

View File

@@ -1,6 +1,7 @@
providers:
config:
class: octodns.provider.yaml.YamlProvider
# This helps us get coverage when printing out provider versions
class: helpers.TestYamlProvider
directory: tests/config
dump:
class: octodns.provider.yaml.YamlProvider
@@ -15,6 +16,9 @@ processors:
# Just testing config so any processor will do
noop:
class: octodns.processor.base.BaseProcessor
test:
# This helps us get coverage when printing out processor versions
class: helpers.TestBaseProcessor
zones:
unit.tests.:

View File

@@ -11,6 +11,7 @@ from logging import getLogger
from octodns.processor.base import BaseProcessor
from octodns.provider.base import BaseProvider
from octodns.provider.yaml import YamlProvider
class SimpleSource(object):
@@ -122,3 +123,11 @@ class PlannableProvider(BaseProvider):
def __repr__(self):
return self.__class__.__name__
class TestYamlProvider(YamlProvider):
pass
class TestBaseProcessor(BaseProcessor):
pass

View File

@@ -8,6 +8,7 @@ from __future__ import absolute_import, division, print_function, \
from os import environ
from os.path import dirname, join
from octodns import __VERSION__
from octodns.manager import _AggregateTarget, MainThreadExecutor, Manager, \
ManagerException
from octodns.processor.base import BaseProcessor
@@ -425,7 +426,7 @@ class TestManager(TestCase):
plan_output_mock = MagicMock()
plan_output_class_mock = MagicMock()
plan_output_class_mock.return_value = plan_output_mock
mock.return_value = plan_output_class_mock
mock.return_value = (plan_output_class_mock, 'ignored', 'ignored')
fh_mock = MagicMock()
Manager(get_config_filename('plan-output-filehandle.yaml')
@@ -441,7 +442,7 @@ class TestManager(TestCase):
def test_processor_config(self):
# Smoke test loading a valid config
manager = Manager(get_config_filename('processors.yaml'))
self.assertEqual(['noop'], list(manager.processors.keys()))
self.assertEqual(['noop', 'test'], list(manager.processors.keys()))
# This zone specifies a valid processor
manager.sync(['unit.tests.'])
@@ -524,6 +525,29 @@ class TestManager(TestCase):
# no plans
self.assertFalse(plans)
def test_try_version(self):
manager = Manager(get_config_filename('simple.yaml'))
class DummyModule(object):
__VERSION__ = '2.3.4'
dummy_module = DummyModule()
# use importlib.metadata.version
self.assertTrue(__VERSION__,
manager._try_version('octodns',
module=dummy_module,
version='1.2.3'))
# use module
self.assertTrue(manager._try_version('doesnt-exist',
module=dummy_module))
# fall back to version, preferred over module
self.assertEqual('1.2.3', manager._try_version('doesnt-exist',
module=dummy_module,
version='1.2.3', ))
class TestMainThreadExecutor(TestCase):