mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2025-09-20 01:06:03 +02:00
filter/domain_redirect_mappings: add auto_build_alias parameter
- Extend filter signature with auto_build_alias flag to control automatic default→canonical alias creation - group_vars/all: introduce AUTO_BUILD_ALIASES variable for global toggle - Update unit tests: adjust calls to new signature and add dedicated test cases for auto_build_aliases=False Ref: conversation https://chatgpt.com/share/68cd512c-c878-800f-bdf2-81737adf7e0e
This commit is contained in:
@@ -7,7 +7,7 @@ class FilterModule(object):
|
|||||||
def filters(self):
|
def filters(self):
|
||||||
return {'domain_mappings': self.domain_mappings}
|
return {'domain_mappings': self.domain_mappings}
|
||||||
|
|
||||||
def domain_mappings(self, apps, PRIMARY_DOMAIN):
|
def domain_mappings(self, apps, primary_domain, auto_build_alias):
|
||||||
"""
|
"""
|
||||||
Build a flat list of redirect mappings for all apps:
|
Build a flat list of redirect mappings for all apps:
|
||||||
- source: each alias domain
|
- source: each alias domain
|
||||||
@@ -43,7 +43,7 @@ class FilterModule(object):
|
|||||||
domains_cfg = cfg.get('server',{}).get('domains',{})
|
domains_cfg = cfg.get('server',{}).get('domains',{})
|
||||||
entry = domains_cfg.get('canonical')
|
entry = domains_cfg.get('canonical')
|
||||||
if entry is None:
|
if entry is None:
|
||||||
canonical_map[app_id] = [default_domain(app_id, PRIMARY_DOMAIN)]
|
canonical_map[app_id] = [default_domain(app_id, primary_domain)]
|
||||||
elif isinstance(entry, dict):
|
elif isinstance(entry, dict):
|
||||||
canonical_map[app_id] = list(entry.values())
|
canonical_map[app_id] = list(entry.values())
|
||||||
elif isinstance(entry, list):
|
elif isinstance(entry, list):
|
||||||
@@ -61,11 +61,11 @@ class FilterModule(object):
|
|||||||
alias_map[app_id] = []
|
alias_map[app_id] = []
|
||||||
continue
|
continue
|
||||||
if isinstance(domains_cfg, dict) and not domains_cfg:
|
if isinstance(domains_cfg, dict) and not domains_cfg:
|
||||||
alias_map[app_id] = [default_domain(app_id, PRIMARY_DOMAIN)]
|
alias_map[app_id] = [default_domain(app_id, primary_domain)]
|
||||||
continue
|
continue
|
||||||
|
|
||||||
aliases = parse_entry(domains_cfg, 'aliases', app_id) or []
|
aliases = parse_entry(domains_cfg, 'aliases', app_id) or []
|
||||||
default = default_domain(app_id, PRIMARY_DOMAIN)
|
default = default_domain(app_id, primary_domain)
|
||||||
has_aliases = 'aliases' in domains_cfg
|
has_aliases = 'aliases' in domains_cfg
|
||||||
has_canonical = 'canonical' in domains_cfg
|
has_canonical = 'canonical' in domains_cfg
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ class FilterModule(object):
|
|||||||
aliases.append(default)
|
aliases.append(default)
|
||||||
elif has_canonical:
|
elif has_canonical:
|
||||||
canon = canonical_map.get(app_id, [])
|
canon = canonical_map.get(app_id, [])
|
||||||
if default not in canon and default not in aliases:
|
if default not in canon and default not in aliases and auto_build_alias:
|
||||||
aliases.append(default)
|
aliases.append(default)
|
||||||
|
|
||||||
alias_map[app_id] = aliases
|
alias_map[app_id] = aliases
|
||||||
@@ -84,7 +84,7 @@ class FilterModule(object):
|
|||||||
mappings = []
|
mappings = []
|
||||||
for app_id, sources in alias_map.items():
|
for app_id, sources in alias_map.items():
|
||||||
canon_list = canonical_map.get(app_id, [])
|
canon_list = canonical_map.get(app_id, [])
|
||||||
target = canon_list[0] if canon_list else default_domain(app_id, PRIMARY_DOMAIN)
|
target = canon_list[0] if canon_list else default_domain(app_id, primary_domain)
|
||||||
for src in sources:
|
for src in sources:
|
||||||
if src == target:
|
if src == target:
|
||||||
# skip self-redirects
|
# skip self-redirects
|
||||||
|
@@ -32,8 +32,10 @@ WEBSOCKET_PROTOCOL: "{{ 'wss' if WEB_PROTOCOL == 'https' else 'ws' }}"
|
|||||||
# WWW-Redirect to None WWW-Domains enabled
|
# WWW-Redirect to None WWW-Domains enabled
|
||||||
WWW_REDIRECT_ENABLED: "{{ ('web-opt-rdr-www' in group_names) | bool }}"
|
WWW_REDIRECT_ENABLED: "{{ ('web-opt-rdr-www' in group_names) | bool }}"
|
||||||
|
|
||||||
|
AUTO_BUILD_ALIASES: False # If enabled it creates an alias domain for each web application by the entity name, recommended to set to false to safge domain space
|
||||||
|
|
||||||
# Domain
|
# Domain
|
||||||
PRIMARY_DOMAIN: "localhost" # Primary Domain of the server
|
PRIMARY_DOMAIN: "localhost" # Primary Domain of the server
|
||||||
|
|
||||||
DNS_PROVIDER: cloudflare # The DNS Provider\Registrar for the domain
|
DNS_PROVIDER: cloudflare # The DNS Provider\Registrar for the domain
|
||||||
|
|
||||||
|
@@ -18,20 +18,20 @@ class TestDomainMappings(unittest.TestCase):
|
|||||||
|
|
||||||
def test_empty_apps(self):
|
def test_empty_apps(self):
|
||||||
apps = {}
|
apps = {}
|
||||||
result = self.filter.domain_mappings(apps, self.primary)
|
result = self.filter.domain_mappings(apps, self.primary, True)
|
||||||
self.assertEqual(result, [])
|
self.assertEqual(result, [])
|
||||||
|
|
||||||
def test_app_without_domains(self):
|
def test_app_without_domains(self):
|
||||||
apps = {'web-app-desktop': {}}
|
apps = {'web-app-desktop': {}}
|
||||||
# no domains key → no mappings
|
# no domains key → no mappings
|
||||||
result = self.filter.domain_mappings(apps, self.primary)
|
result = self.filter.domain_mappings(apps, self.primary, True)
|
||||||
self.assertEqual(result, [])
|
self.assertEqual(result, [])
|
||||||
|
|
||||||
def test_empty_domains_cfg(self):
|
def test_empty_domains_cfg(self):
|
||||||
apps = {'web-app-desktop': {'domains': {}}}
|
apps = {'web-app-desktop': {'domains': {}}}
|
||||||
default = 'desktop.example.com'
|
default = 'desktop.example.com'
|
||||||
expected = []
|
expected = []
|
||||||
result = self.filter.domain_mappings(apps, self.primary)
|
result = self.filter.domain_mappings(apps, self.primary, True)
|
||||||
self.assertEqual(result, expected)
|
self.assertEqual(result, expected)
|
||||||
|
|
||||||
def test_explicit_aliases(self):
|
def test_explicit_aliases(self):
|
||||||
@@ -46,7 +46,7 @@ class TestDomainMappings(unittest.TestCase):
|
|||||||
expected = [
|
expected = [
|
||||||
{'source': 'alias.com', 'target': default},
|
{'source': 'alias.com', 'target': default},
|
||||||
]
|
]
|
||||||
result = self.filter.domain_mappings(apps, self.primary)
|
result = self.filter.domain_mappings(apps, self.primary, True)
|
||||||
# order not important
|
# order not important
|
||||||
self.assertCountEqual(result, expected)
|
self.assertCountEqual(result, expected)
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ class TestDomainMappings(unittest.TestCase):
|
|||||||
expected = [
|
expected = [
|
||||||
{'source': 'desktop.example.com', 'target': 'foo.com'}
|
{'source': 'desktop.example.com', 'target': 'foo.com'}
|
||||||
]
|
]
|
||||||
result = self.filter.domain_mappings(apps, self.primary)
|
result = self.filter.domain_mappings(apps, self.primary, True)
|
||||||
self.assertEqual(result, expected)
|
self.assertEqual(result, expected)
|
||||||
|
|
||||||
def test_canonical_dict(self):
|
def test_canonical_dict(self):
|
||||||
@@ -78,7 +78,7 @@ class TestDomainMappings(unittest.TestCase):
|
|||||||
expected = [
|
expected = [
|
||||||
{'source': 'desktop.example.com', 'target': 'one.com'}
|
{'source': 'desktop.example.com', 'target': 'one.com'}
|
||||||
]
|
]
|
||||||
result = self.filter.domain_mappings(apps, self.primary)
|
result = self.filter.domain_mappings(apps, self.primary, True)
|
||||||
self.assertEqual(result, expected)
|
self.assertEqual(result, expected)
|
||||||
|
|
||||||
def test_multiple_apps(self):
|
def test_multiple_apps(self):
|
||||||
@@ -94,7 +94,7 @@ class TestDomainMappings(unittest.TestCase):
|
|||||||
{'source': 'a1.com', 'target': 'desktop.example.com'},
|
{'source': 'a1.com', 'target': 'desktop.example.com'},
|
||||||
{'source': 'mastodon.example.com', 'target': 'c2.com'},
|
{'source': 'mastodon.example.com', 'target': 'c2.com'},
|
||||||
]
|
]
|
||||||
result = self.filter.domain_mappings(apps, self.primary)
|
result = self.filter.domain_mappings(apps, self.primary, True)
|
||||||
self.assertCountEqual(result, expected)
|
self.assertCountEqual(result, expected)
|
||||||
|
|
||||||
def test_multiple_aliases(self):
|
def test_multiple_aliases(self):
|
||||||
@@ -108,7 +108,7 @@ class TestDomainMappings(unittest.TestCase):
|
|||||||
{'source': 'a1.com', 'target': 'desktop.example.com'},
|
{'source': 'a1.com', 'target': 'desktop.example.com'},
|
||||||
{'source': 'a2.com', 'target': 'desktop.example.com'}
|
{'source': 'a2.com', 'target': 'desktop.example.com'}
|
||||||
]
|
]
|
||||||
result = self.filter.domain_mappings(apps, self.primary)
|
result = self.filter.domain_mappings(apps, self.primary, True)
|
||||||
self.assertCountEqual(result, expected)
|
self.assertCountEqual(result, expected)
|
||||||
|
|
||||||
def test_invalid_aliases_type(self):
|
def test_invalid_aliases_type(self):
|
||||||
@@ -116,8 +116,77 @@ class TestDomainMappings(unittest.TestCase):
|
|||||||
'web-app-desktop': {'server':{'domains': {'aliases': 123}}}
|
'web-app-desktop': {'server':{'domains': {'aliases': 123}}}
|
||||||
}
|
}
|
||||||
with self.assertRaises(AnsibleFilterError):
|
with self.assertRaises(AnsibleFilterError):
|
||||||
self.filter.domain_mappings(apps, self.primary)
|
self.filter.domain_mappings(apps, self.primary, True)
|
||||||
|
|
||||||
|
def test_canonical_not_default_no_autobuild(self):
|
||||||
|
"""
|
||||||
|
When only a canonical different from the default exists and auto_build_aliases is False,
|
||||||
|
we should NOT auto-generate a default alias -> canonical mapping.
|
||||||
|
"""
|
||||||
|
apps = {
|
||||||
|
'web-app-desktop': {
|
||||||
|
'server': {
|
||||||
|
'domains': {'canonical': ['foo.com']}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = self.filter.domain_mappings(apps, self.primary, False)
|
||||||
|
self.assertEqual(result, []) # no auto-added default alias
|
||||||
|
|
||||||
|
def test_aliases_and_canonical_no_autobuild_still_adds_default(self):
|
||||||
|
"""
|
||||||
|
If explicit aliases are present, the filter always appends the default domain
|
||||||
|
to the alias list (to cover 'www'/'root' style defaults), regardless of auto_build_aliases.
|
||||||
|
With a canonical set, both the explicit alias and the default should point to the canonical.
|
||||||
|
"""
|
||||||
|
apps = {
|
||||||
|
'web-app-desktop': {
|
||||||
|
'server': {
|
||||||
|
'domains': {
|
||||||
|
'aliases': ['alias.com'],
|
||||||
|
'canonical': ['foo.com']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expected = [
|
||||||
|
{'source': 'alias.com', 'target': 'foo.com'},
|
||||||
|
{'source': 'desktop.example.com', 'target': 'foo.com'},
|
||||||
|
]
|
||||||
|
result = self.filter.domain_mappings(apps, self.primary, False)
|
||||||
|
self.assertCountEqual(result, expected)
|
||||||
|
|
||||||
|
def test_mixed_apps_no_autobuild(self):
|
||||||
|
"""
|
||||||
|
One app with only canonical (no aliases) and one app with only aliases:
|
||||||
|
- The canonical-only app produces no mappings when auto_build_aliases is False.
|
||||||
|
- The alias-only app maps its aliases to its default domain; default self-mapping is skipped.
|
||||||
|
"""
|
||||||
|
apps = {
|
||||||
|
'web-app-desktop': {
|
||||||
|
'server': {'domains': {'canonical': ['c1.com']}}
|
||||||
|
},
|
||||||
|
'web-app-mastodon': {
|
||||||
|
'server': {'domains': {'aliases': ['m1.com']}}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
expected = [
|
||||||
|
{'source': 'm1.com', 'target': 'mastodon.example.com'},
|
||||||
|
]
|
||||||
|
result = self.filter.domain_mappings(apps, self.primary, False)
|
||||||
|
self.assertCountEqual(result, expected)
|
||||||
|
|
||||||
|
def test_no_domains_key_no_autobuild(self):
|
||||||
|
"""
|
||||||
|
App ohne 'server.domains' erzeugt keine Mappings, unabhängig von auto_build_aliases.
|
||||||
|
"""
|
||||||
|
apps = {
|
||||||
|
'web-app-desktop': {
|
||||||
|
# no 'server' or 'domains'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = self.filter.domain_mappings(apps, self.primary, False)
|
||||||
|
self.assertEqual(result, [])
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
Reference in New Issue
Block a user