From 0f85d27a4d5d3e094a9ec3880a472cf817976abc Mon Sep 17 00:00:00 2001 From: Kevin Veen-Birkenbach Date: Fri, 19 Sep 2025 14:49:02 +0200 Subject: [PATCH] filter/domain_redirect_mappings: add auto_build_alias parameter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- filter_plugins/domain_redirect_mappings.py | 12 +-- group_vars/all/00_general.yml | 4 +- .../filter_plugins/test_domain_mappings.py | 87 +++++++++++++++++-- 3 files changed, 87 insertions(+), 16 deletions(-) diff --git a/filter_plugins/domain_redirect_mappings.py b/filter_plugins/domain_redirect_mappings.py index 81a97649..09008b76 100644 --- a/filter_plugins/domain_redirect_mappings.py +++ b/filter_plugins/domain_redirect_mappings.py @@ -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 diff --git a/group_vars/all/00_general.yml b/group_vars/all/00_general.yml index 64a6536b..882c0d88 100644 --- a/group_vars/all/00_general.yml +++ b/group_vars/all/00_general.yml @@ -32,8 +32,10 @@ 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 +PRIMARY_DOMAIN: "localhost" # Primary Domain of the server DNS_PROVIDER: cloudflare # The DNS Provider\Registrar for the domain diff --git a/tests/unit/filter_plugins/test_domain_mappings.py b/tests/unit/filter_plugins/test_domain_mappings.py index ef212476..299fff9a 100644 --- a/tests/unit/filter_plugins/test_domain_mappings.py +++ b/tests/unit/filter_plugins/test_domain_mappings.py @@ -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()