feat: Phase 2 — remove builtins layer, ServiceRegistry is installed-only
Unit Tests / test (push) Successful in 11m31s
Unit Tests / test (push) Successful in 11m31s
Builtins (email/calendar/files) are no longer baked into the API image. ServiceRegistry now only knows about installed store services. When nothing is installed, Caddy and DNS get no service routes — no hardcoded fallback. Changes: - service_registry.py: remove _BUILTINS_DIR, _builtin_ids, _builtin_manifest, _load_manifest; get() and list_all() now delegate entirely to installed services - caddy_manager.py: remove _build_core_service_routes(); remove hardcoded fallback pairs from _http01_service_pairs(); empty registry → api block only - network_manager.py: _get_service_subdomains() returns [] when no registry - api/services/builtins/: deleted (email, calendar, files manifests) - Tests updated throughout: removed builtin-dependent assertions, added installed-service fixtures, updated fallback expectations to api-only Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+23
-32
@@ -70,20 +70,16 @@ class TestGenerateCaddyfilePicNgo(unittest.TestCase):
|
||||
# ACME staging hook
|
||||
self.assertIn('acme_ca {$ACME_CA_URL}', out)
|
||||
|
||||
def test_pic_ngo_has_subdomain_service_routes(self):
|
||||
def test_pic_ngo_has_api_route_without_registry(self):
|
||||
mgr = _mgr()
|
||||
identity = {'cell_name': 'alpha', 'domain_mode': 'pic_ngo'}
|
||||
out = mgr.generate_caddyfile(identity, [])
|
||||
# Core services get named-matcher subdomain routing
|
||||
self.assertIn('@calendar host calendar.alpha.pic.ngo', out)
|
||||
self.assertIn('reverse_proxy cell-radicale:5232', out)
|
||||
self.assertIn('@mail host mail.alpha.pic.ngo webmail.alpha.pic.ngo', out)
|
||||
self.assertIn('reverse_proxy cell-rainloop:8888', out)
|
||||
self.assertIn('@files host files.alpha.pic.ngo', out)
|
||||
self.assertIn('reverse_proxy cell-filegator:8080', out)
|
||||
self.assertIn('@webdav host webdav.alpha.pic.ngo', out)
|
||||
self.assertIn('reverse_proxy cell-webdav:80', out)
|
||||
# Without a registry only the api block is present
|
||||
self.assertIn('@api host api.alpha.pic.ngo', out)
|
||||
self.assertIn('reverse_proxy cell-api:3000', out)
|
||||
self.assertNotIn('@calendar', out)
|
||||
self.assertNotIn('@mail', out)
|
||||
self.assertNotIn('@files', out)
|
||||
|
||||
|
||||
class TestGenerateCaddyfileCloudflare(unittest.TestCase):
|
||||
@@ -116,9 +112,10 @@ class TestGenerateCaddyfileCloudflare(unittest.TestCase):
|
||||
self.assertNotIn('*.home.local', out)
|
||||
# 'custom_domain' must not appear literally as a key in the output
|
||||
self.assertNotIn('custom_domain', out)
|
||||
# Service subdomains use the correct public domain
|
||||
self.assertIn('@calendar host calendar.home.example.com', out)
|
||||
self.assertIn('@files host files.home.example.com', out)
|
||||
# Without a registry only the api block is emitted for subdomain routing
|
||||
self.assertIn('@api host api.home.example.com', out)
|
||||
self.assertNotIn('@calendar', out)
|
||||
self.assertNotIn('@files', out)
|
||||
|
||||
|
||||
class TestGenerateCaddyfileDuckDns(unittest.TestCase):
|
||||
@@ -128,8 +125,9 @@ class TestGenerateCaddyfileDuckDns(unittest.TestCase):
|
||||
out = mgr.generate_caddyfile(identity, [])
|
||||
self.assertIn('dns duckdns {$DUCKDNS_TOKEN}', out)
|
||||
self.assertIn('*.gamma.duckdns.org', out)
|
||||
self.assertIn('@calendar host calendar.gamma.duckdns.org', out)
|
||||
self.assertIn('@files host files.gamma.duckdns.org', out)
|
||||
self.assertIn('@api host api.gamma.duckdns.org', out)
|
||||
self.assertNotIn('@calendar', out)
|
||||
self.assertNotIn('@files', out)
|
||||
|
||||
|
||||
class TestGenerateCaddyfileHttp01(unittest.TestCase):
|
||||
@@ -150,34 +148,27 @@ class TestGenerateCaddyfileHttp01(unittest.TestCase):
|
||||
self.assertNotIn('dns ', out)
|
||||
# No explicit tls block — Caddy uses HTTP-01 by default.
|
||||
self.assertNotIn('tls {', out)
|
||||
# Core service blocks are always generated
|
||||
self.assertIn('calendar.delta.noip.me {', out)
|
||||
self.assertIn('files.delta.noip.me {', out)
|
||||
self.assertIn('mail.delta.noip.me {', out)
|
||||
self.assertIn('webmail.delta.noip.me {', out)
|
||||
self.assertIn('webdav.delta.noip.me {', out)
|
||||
# Without a registry only the api block is generated
|
||||
self.assertIn('api.delta.noip.me {', out)
|
||||
self.assertIn('reverse_proxy cell-radicale:5232', out)
|
||||
self.assertIn('reverse_proxy cell-filegator:8080', out)
|
||||
# Installed plugin service block
|
||||
self.assertNotIn('calendar.delta.noip.me {', out)
|
||||
self.assertNotIn('files.delta.noip.me {', out)
|
||||
self.assertNotIn('mail.delta.noip.me {', out)
|
||||
# Installed plugin service block still works
|
||||
self.assertIn('chat.delta.noip.me {', out)
|
||||
self.assertIn('reverse_proxy cell-chat:8090', out)
|
||||
|
||||
def test_http01_installed_service_with_core_name_is_skipped(self):
|
||||
"""An installed service named 'calendar' must not produce a duplicate block."""
|
||||
def test_http01_installed_service_with_caddy_route_appears(self):
|
||||
"""An installed service with a caddy_route produces its own per-host block."""
|
||||
mgr = _mgr()
|
||||
identity = {
|
||||
'cell_name': 'delta',
|
||||
'domain_mode': 'http01',
|
||||
'domain_name': 'delta.noip.me',
|
||||
}
|
||||
services = [{'name': 'calendar', 'caddy_route': 'reverse_proxy cell-other:9000'}]
|
||||
services = [{'name': 'notes', 'caddy_route': 'reverse_proxy cell-other:9000'}]
|
||||
out = mgr.generate_caddyfile(identity, services)
|
||||
# Only one calendar block (the core one)
|
||||
self.assertEqual(out.count('calendar.delta.noip.me {'), 1)
|
||||
# The core backend wins
|
||||
self.assertIn('reverse_proxy cell-radicale:5232', out)
|
||||
self.assertNotIn('cell-other:9000', out)
|
||||
self.assertIn('notes.delta.noip.me {', out)
|
||||
self.assertIn('reverse_proxy cell-other:9000', out)
|
||||
|
||||
|
||||
class TestServiceRoutesIncluded(unittest.TestCase):
|
||||
|
||||
Reference in New Issue
Block a user