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:
2025-09-19 14:49:02 +02:00
parent c6677ca61b
commit 0f85d27a4d
3 changed files with 87 additions and 16 deletions

View File

@@ -7,7 +7,7 @@ class FilterModule(object):
def filters(self):
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:
- source: each alias domain
@@ -43,7 +43,7 @@ class FilterModule(object):
domains_cfg = cfg.get('server',{}).get('domains',{})
entry = domains_cfg.get('canonical')
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):
canonical_map[app_id] = list(entry.values())
elif isinstance(entry, list):
@@ -61,11 +61,11 @@ class FilterModule(object):
alias_map[app_id] = []
continue
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
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_canonical = 'canonical' in domains_cfg
@@ -74,7 +74,7 @@ class FilterModule(object):
aliases.append(default)
elif has_canonical:
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)
alias_map[app_id] = aliases
@@ -84,7 +84,7 @@ class FilterModule(object):
mappings = []
for app_id, sources in alias_map.items():
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:
if src == target:
# skip self-redirects

View File

@@ -32,6 +32,8 @@ WEBSOCKET_PROTOCOL: "{{ 'wss' if WEB_PROTOCOL == 'https' else 'ws' }}"
# WWW-Redirect to None WWW-Domains enabled
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
PRIMARY_DOMAIN: "localhost" # Primary Domain of the server

View File

@@ -18,20 +18,20 @@ class TestDomainMappings(unittest.TestCase):
def test_empty_apps(self):
apps = {}
result = self.filter.domain_mappings(apps, self.primary)
result = self.filter.domain_mappings(apps, self.primary, True)
self.assertEqual(result, [])
def test_app_without_domains(self):
apps = {'web-app-desktop': {}}
# 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, [])
def test_empty_domains_cfg(self):
apps = {'web-app-desktop': {'domains': {}}}
default = 'desktop.example.com'
expected = []
result = self.filter.domain_mappings(apps, self.primary)
result = self.filter.domain_mappings(apps, self.primary, True)
self.assertEqual(result, expected)
def test_explicit_aliases(self):
@@ -46,7 +46,7 @@ class TestDomainMappings(unittest.TestCase):
expected = [
{'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
self.assertCountEqual(result, expected)
@@ -61,7 +61,7 @@ class TestDomainMappings(unittest.TestCase):
expected = [
{'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)
def test_canonical_dict(self):
@@ -78,7 +78,7 @@ class TestDomainMappings(unittest.TestCase):
expected = [
{'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)
def test_multiple_apps(self):
@@ -94,7 +94,7 @@ class TestDomainMappings(unittest.TestCase):
{'source': 'a1.com', 'target': 'desktop.example.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)
def test_multiple_aliases(self):
@@ -108,7 +108,7 @@ class TestDomainMappings(unittest.TestCase):
{'source': 'a1.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)
def test_invalid_aliases_type(self):
@@ -116,8 +116,77 @@ class TestDomainMappings(unittest.TestCase):
'web-app-desktop': {'server':{'domains': {'aliases': 123}}}
}
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__":
unittest.main()