Move NPM configuration and client behind plugin boundary

This commit is contained in:
Lyncoln
2026-09-20 22:50:37 -04:00
parent 332f994c26
commit c9a3898055
8 changed files with 289 additions and 256 deletions
+12 -58
View File
@@ -79,15 +79,6 @@ def parse_endpoint_list(
return tuple(endpoints) return tuple(endpoints)
def endpoint_host(endpoint: Endpoint) -> str:
parsed = urlsplit(f"//{endpoint.address}")
if not parsed.hostname:
raise RuntimeError(
f"Invalid endpoint address: {endpoint.address!r}"
)
return parsed.hostname
def env_bool(name: str, default: bool) -> bool: def env_bool(name: str, default: bool) -> bool:
value = os.getenv(name) value = os.getenv(name)
if value is None: if value is None:
@@ -97,64 +88,32 @@ def env_bool(name: str, default: bool) -> bool:
@dataclass(frozen=True) @dataclass(frozen=True)
class AppConfig: class AppConfig:
npm_endpoints: tuple[Endpoint, ...]
proxy_ip: str
proxy_api_port: int
proxy_api_scheme: str
target_ip: str target_ip: str
proxy_verify_tls: bool
domain_matching: bool domain_matching: bool
domain: str | None domain: str | None
include_disabled: bool = False include_disabled: bool = False
@classmethod @classmethod
def from_env(cls, include_disabled: bool = False) -> "AppConfig": def from_env(cls, include_disabled: bool = False) -> "AppConfig":
try:
port = int(os.getenv("PROXY_API_PORT", "81"))
except ValueError as exc:
raise RuntimeError("PROXY_API_PORT must be an integer") from exc
if not 1 <= port <= 65535:
raise RuntimeError("PROXY_API_PORT must be between 1 and 65535")
scheme = os.getenv("PROXY_API_SCHEME", "http").strip().lower()
if scheme not in {"http", "https"}:
raise RuntimeError("PROXY_API_SCHEME must be http or https")
raw_proxy_ip = os.getenv("PROXY_IP")
raw_npm_endpoints = os.getenv("NPM_ENDPOINTS")
if raw_npm_endpoints is None:
if not raw_proxy_ip:
raise RuntimeError(
"NPM_ENDPOINTS or legacy PROXY_IP is required"
)
proxy_ip = normalize_ipv4(raw_proxy_ip, "PROXY_IP")
npm_endpoints = parse_endpoint_list("npm", proxy_ip, port)
else:
npm_endpoints = parse_endpoint_list(
"npm",
raw_npm_endpoints,
port,
)
if raw_proxy_ip:
proxy_ip = normalize_ipv4(raw_proxy_ip, "PROXY_IP")
else:
proxy_ip = endpoint_host(npm_endpoints[0])
raw_target_ip = os.getenv("DNS_TARGET_IP") raw_target_ip = os.getenv("DNS_TARGET_IP")
raw_proxy_ip = os.getenv("PROXY_IP")
if raw_target_ip: if raw_target_ip:
target_ip = normalize_ipv4(raw_target_ip, "DNS_TARGET_IP")
elif raw_proxy_ip:
target_ip = proxy_ip
elif len(npm_endpoints) == 1:
target_ip = normalize_ipv4( target_ip = normalize_ipv4(
endpoint_host(npm_endpoints[0]), raw_target_ip,
"DNS_TARGET_IP", "DNS_TARGET_IP",
) )
elif raw_proxy_ip:
# Deprecated compatibility fallback. Core no longer treats
# PROXY_IP as an NPM endpoint.
target_ip = normalize_ipv4(
raw_proxy_ip,
"PROXY_IP",
)
else: else:
raise RuntimeError( raise RuntimeError(
"DNS_TARGET_IP is required when multiple NPM_ENDPOINTS " "DNS_TARGET_IP is required "
"are configured without legacy PROXY_IP" "(legacy PROXY_IP is also accepted)"
) )
domain_matching = env_bool("DOMAIN_MATCHING", False) domain_matching = env_bool("DOMAIN_MATCHING", False)
@@ -173,12 +132,7 @@ class AppConfig:
) )
return cls( return cls(
npm_endpoints=npm_endpoints,
proxy_ip=proxy_ip,
proxy_api_port=port,
proxy_api_scheme=scheme,
target_ip=target_ip, target_ip=target_ip,
proxy_verify_tls=env_bool("PROXY_VERIFY_TLS", True),
domain_matching=domain_matching, domain_matching=domain_matching,
domain=domain, domain=domain,
include_disabled=include_disabled, include_disabled=include_disabled,
@@ -1,6 +1,7 @@
import os
from typing import Any from typing import Any
from ...adapters import ( from ....adapters import (
CERTIFICATES, CERTIFICATES,
PROXY_HOSTS, PROXY_HOSTS,
ApplyResult, ApplyResult,
@@ -13,18 +14,49 @@ from ...adapters import (
PlanOperation, PlanOperation,
RetireRequest, RetireRequest,
) )
from ...adapter_support import ( from ....adapter_support import (
require_action_endpoint, require_action_endpoint,
require_desired, require_desired,
require_endpoint_plugin, require_endpoint_plugin,
) )
from ...certificates.material import CertificateMaterial from ....certificates.material import CertificateMaterial
from ...npm_client import NpmClient from ....config import parse_endpoint_list
from ...object_normalizer import normalize_discovered_object from ....normalizer import normalize_ipv4
from .client import NpmClient
from ....object_normalizer import normalize_discovered_object
def configured_endpoints(config): def configured_endpoints(config):
return config.npm_endpoints try:
port = int(os.getenv("PROXY_API_PORT", "81"))
except ValueError as exc:
raise RuntimeError(
"PROXY_API_PORT must be an integer"
) from exc
if not 1 <= port <= 65535:
raise RuntimeError(
"PROXY_API_PORT must be between 1 and 65535"
)
raw_endpoints = os.getenv("NPM_ENDPOINTS")
if raw_endpoints is None:
raw_proxy_ip = os.getenv("PROXY_IP")
if not raw_proxy_ip:
raise RuntimeError(
"NPM_ENDPOINTS or legacy PROXY_IP is required"
)
raw_endpoints = normalize_ipv4(
raw_proxy_ip,
"PROXY_IP",
)
return parse_endpoint_list(
"npm",
raw_endpoints,
port,
)
_PROXY_HOST_FIELDS = ( _PROXY_HOST_FIELDS = (
@@ -77,9 +109,9 @@ class Plugin:
def __init__(self, config, endpoint): def __init__(self, config, endpoint):
require_endpoint_plugin(endpoint, self.name) require_endpoint_plugin(endpoint, self.name)
self.config = config self.endpoints = configured_endpoints(config)
self.endpoint = endpoint self.endpoint = endpoint
self.client = NpmClient(config, endpoint) self.client = NpmClient(endpoint)
@staticmethod @staticmethod
def _local_key(row: dict[str, Any], label: str) -> str: def _local_key(row: dict[str, Any], label: str) -> str:
@@ -246,16 +278,13 @@ class Plugin:
source_errors: list[str] = [] source_errors: list[str] = []
for endpoint in sorted( for endpoint in sorted(
self.config.npm_endpoints, self.endpoints,
key=lambda item: item.identity, key=lambda item: item.identity,
): ):
if endpoint.identity == self.endpoint.identity: if endpoint.identity == self.endpoint.identity:
continue continue
client = NpmClient( client = NpmClient(endpoint)
self.config,
endpoint,
)
try: try:
rows = client.certificates() rows = client.certificates()
@@ -7,8 +7,9 @@ from zipfile import BadZipFile, ZipFile
import requests import requests
import urllib3 import urllib3
from .adapters import Endpoint from ....adapters import Endpoint
from .certificates.material import CertificateMaterial from ....certificates.material import CertificateMaterial
from ....config import env_bool
class NpmClient: class NpmClient:
@@ -16,11 +17,9 @@ class NpmClient:
def __init__( def __init__(
self, self,
config, endpoint: Endpoint,
endpoint: Endpoint | None = None,
): ):
self.config = config self.endpoint = endpoint
self.endpoint = endpoint or config.npm_endpoints[0]
if self.endpoint.plugin != "npm": if self.endpoint.plugin != "npm":
raise RuntimeError("NpmClient requires an npm endpoint") raise RuntimeError("NpmClient requires an npm endpoint")
@@ -34,10 +33,25 @@ class NpmClient:
if not self.secret: if not self.secret:
raise RuntimeError("NPM_SECRET is required") raise RuntimeError("NPM_SECRET is required")
self.api_scheme = os.getenv(
"PROXY_API_SCHEME",
"http",
).strip().lower()
if self.api_scheme not in {"http", "https"}:
raise RuntimeError(
"PROXY_API_SCHEME must be http or https"
)
self.verify_tls = env_bool(
"PROXY_VERIFY_TLS",
True,
)
self.session = requests.Session() self.session = requests.Session()
self.token: str | None = None self.token: str | None = None
if not config.proxy_verify_tls: if not self.verify_tls:
urllib3.disable_warnings( urllib3.disable_warnings(
urllib3.exceptions.InsecureRequestWarning urllib3.exceptions.InsecureRequestWarning
) )
@@ -45,7 +59,7 @@ class NpmClient:
@property @property
def api_url(self) -> str: def api_url(self) -> str:
return ( return (
f"{self.config.proxy_api_scheme}://" f"{self.api_scheme}://"
f"{self.endpoint.address}/api" f"{self.endpoint.address}/api"
) )
@@ -57,7 +71,7 @@ class NpmClient:
"secret": self.secret, "secret": self.secret,
}, },
timeout=15, timeout=15,
verify=self.config.proxy_verify_tls, verify=self.verify_tls,
) )
if response.status_code != 200: if response.status_code != 200:
@@ -107,7 +121,7 @@ class NpmClient:
method, method,
f"{self.api_url}{path}", f"{self.api_url}{path}",
timeout=kwargs.pop("timeout", 15), timeout=kwargs.pop("timeout", 15),
verify=self.config.proxy_verify_tls, verify=self.verify_tls,
**kwargs, **kwargs,
) )
+13 -33
View File
@@ -47,28 +47,27 @@ class EndpointParsingTests(unittest.TestCase):
) )
class AppConfigEndpointTests(unittest.TestCase): class AppConfigPolicyTests(unittest.TestCase):
def test_legacy_proxy_ip_remains_supported(self): def test_explicit_dns_target_is_used(self):
with patch.dict( with patch.dict(
os.environ, os.environ,
{"PROXY_IP": "192.168.1.101"}, {
"DNS_TARGET_IP": "192.168.1.101",
"NPM_ENDPOINTS": "192.168.1.3,192.168.1.4",
},
clear=True, clear=True,
): ):
config = AppConfig.from_env() config = AppConfig.from_env()
self.assertEqual( self.assertEqual(
tuple(endpoint.identity for endpoint in config.npm_endpoints), config.target_ip,
("npm://192.168.1.101:81",), "192.168.1.101",
) )
self.assertEqual(config.target_ip, "192.168.1.101")
def test_endpoint_list_controls_api_and_proxy_ip_remains_target(self): def test_legacy_proxy_ip_remains_target_fallback(self):
with patch.dict( with patch.dict(
os.environ, os.environ,
{ {
"NPM_ENDPOINTS": (
"192.168.1.3,192.168.1.4:8081,192.168.1.5"
),
"PROXY_IP": "192.168.1.101", "PROXY_IP": "192.168.1.101",
}, },
clear=True, clear=True,
@@ -76,33 +75,15 @@ class AppConfigEndpointTests(unittest.TestCase):
config = AppConfig.from_env() config = AppConfig.from_env()
self.assertEqual( self.assertEqual(
tuple(endpoint.identity for endpoint in config.npm_endpoints), config.target_ip,
( "192.168.1.101",
"npm://192.168.1.3:81",
"npm://192.168.1.4:8081",
"npm://192.168.1.5:81",
),
) )
self.assertEqual(config.target_ip, "192.168.1.101")
def test_explicit_dns_target_supports_multiple_endpoints(self): def test_dns_target_is_required_without_legacy_fallback(self):
with patch.dict( with patch.dict(
os.environ, os.environ,
{ {
"NPM_ENDPOINTS": "192.168.1.3,192.168.1.4", "NPM_ENDPOINTS": "192.168.1.3",
"DNS_TARGET_IP": "192.168.1.101",
},
clear=True,
):
config = AppConfig.from_env()
self.assertEqual(config.target_ip, "192.168.1.101")
def test_multiple_endpoints_require_target_without_proxy_ip(self):
with patch.dict(
os.environ,
{
"NPM_ENDPOINTS": "192.168.1.3,192.168.1.4",
}, },
clear=True, clear=True,
): ):
@@ -113,6 +94,5 @@ class AppConfigEndpointTests(unittest.TestCase):
AppConfig.from_env() AppConfig.from_env()
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()
+120 -112
View File
@@ -16,73 +16,103 @@ from dns_sync.adapters import (
from dns_sync.certificates.material import CertificateMaterial from dns_sync.certificates.material import CertificateMaterial
from dns_sync.config import AppConfig from dns_sync.config import AppConfig
from dns_sync.object_normalizer import normalize_discovered_object from dns_sync.object_normalizer import normalize_discovered_object
from dns_sync.registry import load_adapter from dns_sync.registry import (
configured_adapter_endpoints,
load_adapter,
)
class NpmAdapterTests(unittest.TestCase): class NpmAdapterTests(unittest.TestCase):
def config(self): @staticmethod
with patch.dict( def npm_env(
endpoints="192.168.1.3,192.168.1.4",
):
return patch.dict(
os.environ, os.environ,
{ {
"NPM_ENDPOINTS": "192.168.1.3,192.168.1.4", "NPM_ENDPOINTS": endpoints,
"DNS_TARGET_IP": "192.168.1.101", "DNS_TARGET_IP": "192.168.1.101",
}, },
clear=True, clear=True,
): )
def config(self):
with self.npm_env():
return AppConfig.from_env() return AppConfig.from_env()
def adapter(self, client): def adapter(self, client):
config = self.config() with self.npm_env():
with patch( config = AppConfig.from_env()
"dns_sync.plugins.adapters.npm.NpmClient", endpoints = configured_adapter_endpoints(
return_value=client,
):
return load_adapter(
"npm", "npm",
config, config,
config.npm_endpoints[0],
) )
with patch(
"dns_sync.plugins.adapters.npm.NpmClient",
return_value=client,
):
return load_adapter(
"npm",
config,
endpoints[0],
)
def target_adapter_with_sources( def target_adapter_with_sources(
self, self,
target_client, target_client,
source_clients, source_clients,
): ):
config = self.config() with self.npm_env():
config = AppConfig.from_env()
with patch( endpoints = configured_adapter_endpoints(
"dns_sync.plugins.adapters.npm.NpmClient",
return_value=target_client,
):
adapter = load_adapter(
"npm", "npm",
config, config,
config.npm_endpoints[1],
) )
with patch(
"dns_sync.plugins.adapters.npm.NpmClient",
return_value=target_client,
):
adapter = load_adapter(
"npm",
config,
endpoints[1],
)
client_by_identity = { client_by_identity = {
endpoint_id: client endpoint_id: client
for endpoint_id, client in source_clients.items() for endpoint_id, client in source_clients.items()
} }
def client_factory(config, endpoint): def client_factory(endpoint):
return client_by_identity[endpoint.identity] return client_by_identity[endpoint.identity]
return adapter, client_factory return adapter, client_factory
def test_registry_creates_one_adapter_per_endpoint(self): def test_registry_creates_one_adapter_per_endpoint(self):
config = self.config()
first_client = Mock() first_client = Mock()
second_client = Mock() second_client = Mock()
with patch( with self.npm_env():
"dns_sync.plugins.adapters.npm.NpmClient", config = AppConfig.from_env()
side_effect=[first_client, second_client], endpoints = configured_adapter_endpoints(
) as client_class: "npm",
adapters = [ config,
load_adapter("npm", config, endpoint) )
for endpoint in config.npm_endpoints
] with patch(
"dns_sync.plugins.adapters.npm.NpmClient",
side_effect=[first_client, second_client],
) as client_class:
adapters = [
load_adapter(
"npm",
config,
endpoint,
)
for endpoint in endpoints
]
self.assertEqual( self.assertEqual(
tuple(adapter.endpoint.identity for adapter in adapters), tuple(adapter.endpoint.identity for adapter in adapters),
@@ -517,25 +547,14 @@ class NpmAdapterTests(unittest.TestCase):
source_client.certificate_material.assert_not_called() source_client.certificate_material.assert_not_called()
def test_certificate_material_source_rejects_conflicting_material(self): def test_certificate_material_source_rejects_conflicting_material(self):
config = self.config() with self.npm_env(
"192.168.1.3,192.168.1.4,192.168.1.5"
# Add a third endpoint for this ambiguity test. ):
endpoint_type = type(config.npm_endpoints[0]) config = AppConfig.from_env()
third_endpoint = endpoint_type( endpoints = configured_adapter_endpoints(
"npm", "npm",
"192.168.1.5:81", config,
) )
config = __import__(
"dataclasses"
).replace(
config,
npm_endpoints=(
config.npm_endpoints[0],
config.npm_endpoints[1],
third_endpoint,
),
)
target_client = Mock() target_client = Mock()
source_a = Mock() source_a = Mock()
@@ -568,22 +587,25 @@ class NpmAdapterTests(unittest.TestCase):
) )
) )
with patch( with self.npm_env(
"dns_sync.plugins.adapters.npm.NpmClient", "192.168.1.3,192.168.1.4,192.168.1.5"
return_value=target_client,
): ):
adapter = load_adapter( with patch(
"npm", "dns_sync.plugins.adapters.npm.NpmClient",
config, return_value=target_client,
config.npm_endpoints[1], ):
) adapter = load_adapter(
"npm",
config,
endpoints[1],
)
clients = { clients = {
"npm://192.168.1.3:81": source_a, "npm://192.168.1.3:81": source_a,
"npm://192.168.1.5:81": source_b, "npm://192.168.1.5:81": source_b,
} }
def client_factory(config, endpoint): def client_factory(endpoint):
return clients[endpoint.identity] return clients[endpoint.identity]
desired = NormalizedObject( desired = NormalizedObject(
@@ -607,24 +629,14 @@ class NpmAdapterTests(unittest.TestCase):
adapter._certificate_material_source(desired) adapter._certificate_material_source(desired)
def test_certificate_material_source_accepts_identical_replicas(self): def test_certificate_material_source_accepts_identical_replicas(self):
config = self.config() with self.npm_env(
"192.168.1.3,192.168.1.4,192.168.1.5"
endpoint_type = type(config.npm_endpoints[0]) ):
third_endpoint = endpoint_type( config = AppConfig.from_env()
"npm", endpoints = configured_adapter_endpoints(
"192.168.1.5:81", "npm",
) config,
)
config = __import__(
"dataclasses"
).replace(
config,
npm_endpoints=(
config.npm_endpoints[0],
config.npm_endpoints[1],
third_endpoint,
),
)
target_client = Mock() target_client = Mock()
source_a = Mock() source_a = Mock()
@@ -660,22 +672,25 @@ class NpmAdapterTests(unittest.TestCase):
source_a.certificate_material.return_value = common_material source_a.certificate_material.return_value = common_material
source_b.certificate_material.return_value = common_material source_b.certificate_material.return_value = common_material
with patch( with self.npm_env(
"dns_sync.plugins.adapters.npm.NpmClient", "192.168.1.3,192.168.1.4,192.168.1.5"
return_value=target_client,
): ):
adapter = load_adapter( with patch(
"npm", "dns_sync.plugins.adapters.npm.NpmClient",
config, return_value=target_client,
config.npm_endpoints[1], ):
) adapter = load_adapter(
"npm",
config,
endpoints[1],
)
clients = { clients = {
"npm://192.168.1.3:81": source_a, "npm://192.168.1.3:81": source_a,
"npm://192.168.1.5:81": source_b, "npm://192.168.1.5:81": source_b,
} }
def client_factory(config, endpoint): def client_factory(endpoint):
return clients[endpoint.identity] return clients[endpoint.identity]
desired = NormalizedObject( desired = NormalizedObject(
@@ -699,24 +714,14 @@ class NpmAdapterTests(unittest.TestCase):
self.assertEqual(resolved, common_material) self.assertEqual(resolved, common_material)
def test_certificate_material_source_tolerates_unavailable_unrelated_source(self): def test_certificate_material_source_tolerates_unavailable_unrelated_source(self):
config = self.config() with self.npm_env(
"192.168.1.3,192.168.1.4,192.168.1.5"
endpoint_type = type(config.npm_endpoints[0]) ):
third_endpoint = endpoint_type( config = AppConfig.from_env()
"npm", endpoints = configured_adapter_endpoints(
"192.168.1.5:81", "npm",
) config,
)
config = __import__(
"dataclasses"
).replace(
config,
npm_endpoints=(
config.npm_endpoints[0],
config.npm_endpoints[1],
third_endpoint,
),
)
target_client = Mock() target_client = Mock()
unavailable = Mock() unavailable = Mock()
@@ -743,22 +748,25 @@ class NpmAdapterTests(unittest.TestCase):
) )
available.certificate_material.return_value = material available.certificate_material.return_value = material
with patch( with self.npm_env(
"dns_sync.plugins.adapters.npm.NpmClient", "192.168.1.3,192.168.1.4,192.168.1.5"
return_value=target_client,
): ):
adapter = load_adapter( with patch(
"npm", "dns_sync.plugins.adapters.npm.NpmClient",
config, return_value=target_client,
config.npm_endpoints[1], ):
) adapter = load_adapter(
"npm",
config,
endpoints[1],
)
clients = { clients = {
"npm://192.168.1.3:81": unavailable, "npm://192.168.1.3:81": unavailable,
"npm://192.168.1.5:81": available, "npm://192.168.1.5:81": available,
} }
def client_factory(config, endpoint): def client_factory(endpoint):
return clients[endpoint.identity] return clients[endpoint.identity]
desired = NormalizedObject( desired = NormalizedObject(
+74 -27
View File
@@ -5,48 +5,49 @@ from zipfile import ZipFile
from unittest.mock import Mock, patch from unittest.mock import Mock, patch
from dns_sync.adapters import Endpoint from dns_sync.adapters import Endpoint
from dns_sync.config import AppConfig
from dns_sync.certificates.material import CertificateMaterial from dns_sync.certificates.material import CertificateMaterial
from dns_sync.npm_client import NpmClient from dns_sync.plugins.adapters.npm.client import NpmClient
class NpmClientEndpointTests(unittest.TestCase): class NpmClientEndpointTests(unittest.TestCase):
def config(self): def test_client_uses_explicit_endpoint(self):
return AppConfig.from_env()
def test_default_client_uses_first_configured_endpoint(self):
with patch.dict( with patch.dict(
os.environ, os.environ,
{ {
"NPM_ENDPOINTS": "192.168.1.3,192.168.1.4:8081",
"DNS_TARGET_IP": "192.168.1.101",
"NPM_IDENTITY": "test@example.com", "NPM_IDENTITY": "test@example.com",
"NPM_SECRET": "not-a-real-secret", "NPM_SECRET": "not-a-real-secret",
}, },
clear=True, clear=True,
): ):
config = self.config() endpoint = Endpoint(
"npm",
"192.168.1.3:81",
)
with patch( with patch(
"dns_sync.npm_client.requests.Session.request" "dns_sync.plugins.adapters.npm.client.requests.Session.request"
) as request: ) as request:
client = NpmClient(config) client = NpmClient(endpoint)
request.assert_not_called() request.assert_not_called()
self.assertEqual(client.api_url, "http://192.168.1.3:81/api") self.assertEqual(
client.api_url,
"http://192.168.1.3:81/api",
)
def test_client_can_select_specific_configured_endpoint(self): def test_client_can_select_specific_endpoint(self):
with patch.dict( with patch.dict(
os.environ, os.environ,
{ {
"NPM_ENDPOINTS": "192.168.1.3,192.168.1.4:8081",
"DNS_TARGET_IP": "192.168.1.101",
"NPM_IDENTITY": "test@example.com", "NPM_IDENTITY": "test@example.com",
"NPM_SECRET": "not-a-real-secret", "NPM_SECRET": "not-a-real-secret",
}, },
clear=True, clear=True,
): ):
config = self.config() endpoint = Endpoint(
client = NpmClient(config, config.npm_endpoints[1]) "npm",
"192.168.1.4:8081",
)
client = NpmClient(endpoint)
self.assertEqual( self.assertEqual(
client.api_url, client.api_url,
@@ -61,19 +62,66 @@ class NpmClientEndpointTests(unittest.TestCase):
with patch.dict( with patch.dict(
os.environ, os.environ,
{ {
"PROXY_IP": "192.168.1.101",
"NPM_IDENTITY": "test@example.com", "NPM_IDENTITY": "test@example.com",
"NPM_SECRET": "not-a-real-secret", "NPM_SECRET": "not-a-real-secret",
}, },
clear=True, clear=True,
): ):
config = self.config() endpoint = Endpoint(
endpoint = Endpoint("verizon", "192.168.1.1") "verizon",
"192.168.1.1:443",
)
with self.assertRaisesRegex(RuntimeError, "npm endpoint"): with self.assertRaisesRegex(
NpmClient(config, endpoint) RuntimeError,
"npm endpoint",
):
NpmClient(endpoint)
def test_client_rejects_invalid_api_scheme(self):
with patch.dict(
os.environ,
{
"NPM_IDENTITY": "test@example.com",
"NPM_SECRET": "not-a-real-secret",
"PROXY_API_SCHEME": "ftp",
},
clear=True,
):
with self.assertRaisesRegex(
RuntimeError,
"PROXY_API_SCHEME",
):
NpmClient(
Endpoint(
"npm",
"192.168.1.3:81",
)
)
def test_client_owns_tls_verification_setting(self):
with patch.dict(
os.environ,
{
"NPM_IDENTITY": "test@example.com",
"NPM_SECRET": "not-a-real-secret",
"PROXY_API_SCHEME": "https",
"PROXY_VERIFY_TLS": "false",
},
clear=True,
):
client = NpmClient(
Endpoint(
"npm",
"192.168.1.3:81",
)
)
self.assertEqual(
client.api_url,
"https://192.168.1.3:81/api",
)
self.assertFalse(client.verify_tls)
class NpmClientMutationTests(unittest.TestCase): class NpmClientMutationTests(unittest.TestCase):
@@ -81,17 +129,16 @@ class NpmClientMutationTests(unittest.TestCase):
with patch.dict( with patch.dict(
os.environ, os.environ,
{ {
"NPM_ENDPOINTS": "192.168.1.3",
"DNS_TARGET_IP": "192.168.1.101",
"NPM_IDENTITY": "test@example.com", "NPM_IDENTITY": "test@example.com",
"NPM_SECRET": "not-a-real-secret", "NPM_SECRET": "not-a-real-secret",
}, },
clear=True, clear=True,
): ):
config = AppConfig.from_env()
return NpmClient( return NpmClient(
config, Endpoint(
config.npm_endpoints[0], "npm",
"192.168.1.3:81",
)
) )
@staticmethod @staticmethod
+2 -3
View File
@@ -228,14 +228,13 @@ class PluginRegistryTests(unittest.TestCase):
"npm", "npm",
"192.168.1.3:81", "192.168.1.3:81",
) )
config = SimpleNamespace( config = SimpleNamespace()
npm_endpoints=(endpoint,),
)
with patch.dict( with patch.dict(
os.environ, os.environ,
{ {
"ENABLED_PLUGINS": "npm", "ENABLED_PLUGINS": "npm",
"NPM_ENDPOINTS": "192.168.1.3",
}, },
clear=True, clear=True,
): ):
+2
View File
@@ -24,6 +24,7 @@ class VerizonAdapterTests(unittest.TestCase):
with patch.dict( with patch.dict(
os.environ, os.environ,
{ {
"DNS_TARGET_IP": "192.168.1.101",
"NPM_ENDPOINTS": "192.168.1.3", "NPM_ENDPOINTS": "192.168.1.3",
"VERIZON_ENDPOINTS": "192.168.1.1", "VERIZON_ENDPOINTS": "192.168.1.1",
"ENABLED_PLUGINS": "npm,verizon", "ENABLED_PLUGINS": "npm,verizon",
@@ -141,6 +142,7 @@ class VerizonAdapterTests(unittest.TestCase):
with patch.dict( with patch.dict(
os.environ, os.environ,
{ {
"DNS_TARGET_IP": "192.168.1.101",
"NPM_ENDPOINTS": "192.168.1.3", "NPM_ENDPOINTS": "192.168.1.3",
"VERIZON_ENDPOINTS": "192.168.1.1:8443", "VERIZON_ENDPOINTS": "192.168.1.1:8443",
"ENABLED_PLUGINS": "npm,verizon", "ENABLED_PLUGINS": "npm,verizon",