Add external adapter plugin boundary
This commit is contained in:
@@ -164,6 +164,7 @@ class ReconcileCliTests(unittest.TestCase):
|
||||
"NPM_ENDPOINTS": "192.168.1.3",
|
||||
"VERIZON_ENDPOINTS": "192.168.1.1",
|
||||
"DNS_TARGET_IP": "192.168.1.101",
|
||||
"ENABLED_PLUGINS": "npm,verizon",
|
||||
},
|
||||
clear=True,
|
||||
), patch(
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
import os
|
||||
import tempfile
|
||||
import textwrap
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import patch
|
||||
|
||||
from dns_sync.adapters import Endpoint
|
||||
from dns_sync.registry import (
|
||||
enabled_plugins,
|
||||
load_adapter,
|
||||
)
|
||||
|
||||
|
||||
class PluginRegistryTests(unittest.TestCase):
|
||||
def test_default_enabled_plugin_is_npm(self):
|
||||
with patch.dict(os.environ, {}, clear=True):
|
||||
self.assertEqual(
|
||||
enabled_plugins(),
|
||||
frozenset({"npm"}),
|
||||
)
|
||||
|
||||
def test_enabled_plugins_are_explicit(self):
|
||||
with patch.dict(
|
||||
os.environ,
|
||||
{
|
||||
"ENABLED_PLUGINS": (
|
||||
"npm,verizon"
|
||||
)
|
||||
},
|
||||
clear=True,
|
||||
):
|
||||
self.assertEqual(
|
||||
enabled_plugins(),
|
||||
frozenset(
|
||||
{
|
||||
"npm",
|
||||
"verizon",
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
def test_duplicate_enabled_plugin_is_rejected(self):
|
||||
with patch.dict(
|
||||
os.environ,
|
||||
{
|
||||
"ENABLED_PLUGINS": "npm,npm",
|
||||
},
|
||||
clear=True,
|
||||
):
|
||||
with self.assertRaisesRegex(
|
||||
RuntimeError,
|
||||
"duplicate plugin",
|
||||
):
|
||||
enabled_plugins()
|
||||
|
||||
def test_disabled_adapter_is_not_loaded(self):
|
||||
config = SimpleNamespace()
|
||||
endpoint = Endpoint(
|
||||
"verizon",
|
||||
"192.168.1.1:443",
|
||||
)
|
||||
|
||||
with patch.dict(
|
||||
os.environ,
|
||||
{
|
||||
"ENABLED_PLUGINS": "npm",
|
||||
},
|
||||
clear=True,
|
||||
):
|
||||
with self.assertRaisesRegex(
|
||||
RuntimeError,
|
||||
"is not enabled",
|
||||
):
|
||||
load_adapter(
|
||||
"verizon",
|
||||
config,
|
||||
endpoint,
|
||||
)
|
||||
|
||||
def test_external_adapter_is_loaded(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = Path(directory)
|
||||
plugin = root / "example"
|
||||
plugin.mkdir()
|
||||
|
||||
(plugin / "__init__.py").write_text(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
class Plugin:
|
||||
name = "example"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
config,
|
||||
endpoint,
|
||||
):
|
||||
self.config = config
|
||||
self.endpoint = endpoint
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
config = SimpleNamespace()
|
||||
endpoint = Endpoint(
|
||||
"example",
|
||||
"example.test:443",
|
||||
)
|
||||
|
||||
with patch.dict(
|
||||
os.environ,
|
||||
{
|
||||
"ENABLED_PLUGINS": (
|
||||
"example"
|
||||
),
|
||||
"DNS_SYNC_PLUGIN_PATH": (
|
||||
directory
|
||||
),
|
||||
},
|
||||
clear=True,
|
||||
):
|
||||
adapter = load_adapter(
|
||||
"example",
|
||||
config,
|
||||
endpoint,
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
adapter.name,
|
||||
"example",
|
||||
)
|
||||
self.assertEqual(
|
||||
adapter.endpoint,
|
||||
endpoint,
|
||||
)
|
||||
|
||||
def test_external_adapter_can_import_plugin_submodule(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = Path(directory)
|
||||
plugin = root / "example_package"
|
||||
plugin.mkdir()
|
||||
|
||||
(plugin / "helper.py").write_text(
|
||||
"VALUE = 'loaded-from-submodule'\n"
|
||||
)
|
||||
|
||||
(plugin / "__init__.py").write_text(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
from .helper import VALUE
|
||||
|
||||
class Plugin:
|
||||
name = "example_package"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
config,
|
||||
endpoint,
|
||||
):
|
||||
self.config = config
|
||||
self.endpoint = endpoint
|
||||
self.value = VALUE
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
config = SimpleNamespace()
|
||||
endpoint = Endpoint(
|
||||
"example_package",
|
||||
"example.test:443",
|
||||
)
|
||||
|
||||
with patch.dict(
|
||||
os.environ,
|
||||
{
|
||||
"ENABLED_PLUGINS": (
|
||||
"example_package"
|
||||
),
|
||||
"DNS_SYNC_PLUGIN_PATH": directory,
|
||||
},
|
||||
clear=True,
|
||||
):
|
||||
adapter = load_adapter(
|
||||
"example_package",
|
||||
config,
|
||||
endpoint,
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
adapter.value,
|
||||
"loaded-from-submodule",
|
||||
)
|
||||
|
||||
def test_missing_enabled_adapter_fails_closed(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
config = SimpleNamespace()
|
||||
endpoint = Endpoint(
|
||||
"missing",
|
||||
"example.test:443",
|
||||
)
|
||||
|
||||
with patch.dict(
|
||||
os.environ,
|
||||
{
|
||||
"ENABLED_PLUGINS": "missing",
|
||||
"DNS_SYNC_PLUGIN_PATH": (
|
||||
directory
|
||||
),
|
||||
},
|
||||
clear=True,
|
||||
):
|
||||
with self.assertRaisesRegex(
|
||||
RuntimeError,
|
||||
"Unknown adapter plugin",
|
||||
):
|
||||
load_adapter(
|
||||
"missing",
|
||||
config,
|
||||
endpoint,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -22,6 +22,8 @@ class VerizonAdapterTests(unittest.TestCase):
|
||||
{
|
||||
"NPM_ENDPOINTS": "192.168.1.3",
|
||||
"VERIZON_ENDPOINTS": "192.168.1.1",
|
||||
"ENABLED_PLUGINS": "npm,verizon",
|
||||
"DNS_SYNC_PLUGIN_PATH": "external_plugins",
|
||||
},
|
||||
clear=True,
|
||||
):
|
||||
@@ -29,16 +31,37 @@ class VerizonAdapterTests(unittest.TestCase):
|
||||
|
||||
def adapter(self, provider):
|
||||
config = self.config()
|
||||
with patch(
|
||||
"dns_sync.plugins.adapters.verizon.VerizonProvider",
|
||||
return_value=provider,
|
||||
|
||||
import dns_sync.registry as registry
|
||||
|
||||
with patch.dict(
|
||||
os.environ,
|
||||
{
|
||||
"ENABLED_PLUGINS": "npm,verizon",
|
||||
"DNS_SYNC_PLUGIN_PATH": "external_plugins",
|
||||
},
|
||||
clear=False,
|
||||
):
|
||||
return load_adapter(
|
||||
"verizon",
|
||||
config,
|
||||
config.verizon_endpoints[0],
|
||||
module = registry._load_external_adapter_module(
|
||||
"verizon"
|
||||
)
|
||||
|
||||
if module is None:
|
||||
raise AssertionError(
|
||||
"external Verizon adapter was not found"
|
||||
)
|
||||
|
||||
with patch.object(
|
||||
module,
|
||||
"VerizonProvider",
|
||||
return_value=provider,
|
||||
):
|
||||
return load_adapter(
|
||||
"verizon",
|
||||
config,
|
||||
config.verizon_endpoints[0],
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def dns_object(
|
||||
hostname="app.example.com",
|
||||
@@ -85,6 +108,8 @@ class VerizonAdapterTests(unittest.TestCase):
|
||||
{
|
||||
"NPM_ENDPOINTS": "192.168.1.3",
|
||||
"VERIZON_ENDPOINTS": "192.168.1.1:8443",
|
||||
"ENABLED_PLUGINS": "npm,verizon",
|
||||
"DNS_SYNC_PLUGIN_PATH": "external_plugins",
|
||||
"ROUTER_URL": "https://192.168.1.254",
|
||||
"ROUTER_PASSWORD": "not-a-real-password",
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user