refactor(dns): replace sys-dns-parent-hosts with sys-dns-wildcards; emit only *.parent wildcards from CURRENT_PLAY_DOMAINS_ALL

Rename filter parent_build_records→wildcard_records; create only wildcard (*.parent) A/AAAA records (no base/apex); switch to CURRENT_PLAY_DOMAINS_ALL; update vars to SYN_DNS_WILDCARD_RECORDS; adjust role/task names, defaults, and docs; add unittest expecting *.a.b from www.a.b.example.com. See: https://chatgpt.com/share/68c35dc1-7170-800f-8fbe-772e61780597
This commit is contained in:
2025-09-12 01:40:06 +02:00
parent feee3fd71f
commit 206b3eadbc
16 changed files with 289 additions and 250 deletions

View File

@@ -1,131 +0,0 @@
import os
import sys
import unittest
# Make the filter plugin importable
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../../.."))
FILTER_PATH = os.path.join(ROOT, "roles", "sys-dns-parent-hosts", "filter_plugins")
if FILTER_PATH not in sys.path:
sys.path.insert(0, FILTER_PATH)
from parent_dns import parent_build_records # noqa: E402
def has_record(records, rtype, name, zone):
"""True if an exact (type, name, zone) record exists."""
return any(
r.get("type") == rtype and r.get("name") == name and r.get("zone") == zone
for r in records
)
def has_name_or_wildcard(records, rtype, label, zone):
"""True if either <label> or *.<label> exists for (type, zone)."""
return has_record(records, rtype, label, zone) or has_record(
records, rtype, f"*.{label}", zone
)
class TestParentDNS(unittest.TestCase):
def test_end_to_end_with_ipv6(self):
current = {
"web-app-foo": [
"example.com",
"wiki.example.com",
"c.wiki.example.com",
"a.b.example.com",
],
"web-app-bar": ["foo.other.com"], # different apex -> ignored
}
recs = parent_build_records(
current_play_domains=current,
apex="example.com",
ip4="192.0.2.10",
ip6="2001:db8::10", # AAAA may or may not be emitted by role; treat as optional
proxied=True,
explicit_domains=None,
include_apex=True,
min_child_depth=2,
)
# Apex must resolve
self.assertTrue(has_record(recs, "A", "@", "example.com"))
# Parents may be plain or wildcard (or both)
self.assertTrue(has_name_or_wildcard(recs, "A", "wiki", "example.com"))
self.assertTrue(has_name_or_wildcard(recs, "A", "b", "example.com"))
# AAAA optional: if present, at least apex AAAA must exist
if any(r.get("type") == "AAAA" for r in recs):
self.assertTrue(has_record(recs, "AAAA", "@", "example.com"))
# Proxied flag is propagated
self.assertTrue(all(r.get("proxied") is True for r in recs if r["type"] in ("A", "AAAA")))
def test_explicit_domains_without_ipv6(self):
explicit = ["example.com", "c.wiki.example.com", "x.y.example.com"]
recs = parent_build_records(
current_play_domains={"ignore": ["foo.example.com"]},
apex="example.com",
ip4="198.51.100.5",
ip6="", # No IPv6 -> no AAAA expected
proxied=False,
explicit_domains=explicit,
include_apex=True,
min_child_depth=2,
)
# Apex must resolve
self.assertTrue(has_record(recs, "A", "@", "example.com"))
# Parents may be plain or wildcard
self.assertTrue(has_name_or_wildcard(recs, "A", "wiki", "example.com"))
self.assertTrue(has_name_or_wildcard(recs, "A", "y", "example.com"))
# No IPv6 supplied -> there should be no AAAA records
self.assertFalse(any(r.get("type") == "AAAA" for r in recs))
def test_current_play_domains_may_contain_dicts(self):
# Dict values with strings and lists inside must be accepted and flattened.
current = {
"web-app-foo": {
"prod": "wiki.example.com",
"preview": ["c.wiki.example.com"],
},
"web-app-bar": ["irrelevant.other.com"], # different apex, ignored
}
recs = parent_build_records(
current_play_domains=current,
apex="example.com",
ip4="203.0.113.7",
ip6=None,
proxied=False,
explicit_domains=None,
include_apex=True,
min_child_depth=2,
)
self.assertTrue(has_record(recs, "A", "@", "example.com"))
self.assertTrue(has_name_or_wildcard(recs, "A", "wiki", "example.com"))
def test_invalid_inputs_raise(self):
with self.assertRaises(Exception):
parent_build_records(
current_play_domains={"ok": ["example.com"]},
apex="", # invalid apex
ip4="192.0.2.1",
)
with self.assertRaises(Exception):
parent_build_records(
current_play_domains={"ok": ["example.com"]},
apex="example.com",
ip4="", # required
)
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,211 @@
# tests/unit/roles/sys-dns-wildcards/filter_plugins/test_wildcard_dns.py
import unittest
import importlib.util
from pathlib import Path
def _load_module():
"""
Load the wildcard_dns filter plugin from:
roles/sys-dns-wildcards/filter_plugins/wildcard_dns.py
"""
here = Path(__file__).resolve()
# Go up to repository root (…/tests/unit/roles/… → 5 levels up)
repo_root = here.parents[5] if len(here.parents) >= 6 else here.parents[0]
path = repo_root / "roles" / "sys-dns-wildcards" / "filter_plugins" / "wildcard_dns.py"
if not path.exists():
raise FileNotFoundError(f"Could not find {path}")
spec = importlib.util.spec_from_file_location("wildcard_dns", path)
mod = importlib.util.module_from_spec(spec)
assert spec and spec.loader
spec.loader.exec_module(mod) # type: ignore[attr-defined]
return mod
_wildcard_dns = _load_module()
def _get_filter():
"""Return the wildcard_records filter function from the plugin."""
fm = _wildcard_dns.FilterModule()
filters = fm.filters()
if "wildcard_records" not in filters:
raise AssertionError("wildcard_records filter not found")
return filters["wildcard_records"]
def _as_set(records):
"""Normalize records for order-independent comparison."""
return {
(r.get("type"), r.get("name"), r.get("content"), bool(r.get("proxied")))
for r in records
}
class TestWildcardDNS(unittest.TestCase):
def setUp(self):
self.wildcard_records = _get_filter()
def test_only_wildcards_no_apex_or_base(self):
apex = "example.com"
cpda = {
"svc-a": ["c.wiki.example.com", "a.b.example.com"],
"svc-b": {"extra": ["www.a.b.example.com"]},
"svc-c": "example.com",
}
recs = self.wildcard_records(
current_play_domains_all=cpda,
apex=apex,
ip4="203.0.113.10",
ip6="2606:4700:4700::1111",
proxied=True,
explicit_domains=None,
min_child_depth=2,
ipv6_enabled=True,
)
got = _as_set(recs)
expected = {
("A", "*.wiki", "203.0.113.10", True),
("AAAA", "*.wiki", "2606:4700:4700::1111", True),
("A", "*.b", "203.0.113.10", True),
("AAAA", "*.b", "2606:4700:4700::1111", True),
# now included because www.a.b.example.com promotes a.b.example.com as a parent
("A", "*.a.b", "203.0.113.10", True),
("AAAA", "*.a.b", "2606:4700:4700::1111", True),
}
self.assertEqual(got, expected)
def test_min_child_depth_prevents_apex_wildcard(self):
apex = "example.com"
cpda = {"svc": ["x.example.com"]} # depth = 1
recs = self.wildcard_records(
current_play_domains_all=cpda,
apex=apex,
ip4="198.51.100.42",
ip6="2606:4700:4700::1111",
proxied=False,
explicit_domains=None,
min_child_depth=2, # requires >= 2 → no parent derived
ipv6_enabled=True,
)
self.assertEqual(recs, [])
def test_ipv6_disabled_and_private_ipv6_filtered(self):
apex = "example.com"
cpda = {"svc": ["a.b.example.com"]}
# IPv6 disabled → only A record
recs1 = self.wildcard_records(
current_play_domains_all=cpda,
apex=apex,
ip4="203.0.113.9",
ip6="2606:4700:4700::1111",
proxied=False,
explicit_domains=None,
min_child_depth=2,
ipv6_enabled=False,
)
self.assertEqual(_as_set(recs1), {("A", "*.b", "203.0.113.9", False)})
# IPv6 enabled but ULA (not global) → skip AAAA
recs2 = self.wildcard_records(
current_play_domains_all=cpda,
apex=apex,
ip4="203.0.113.9",
ip6="fd00::1",
proxied=False,
explicit_domains=None,
min_child_depth=2,
ipv6_enabled=True,
)
self.assertEqual(_as_set(recs2), {("A", "*.b", "203.0.113.9", False)})
def test_proxied_flag_true_is_set(self):
recs = self.wildcard_records(
current_play_domains_all={"svc": ["a.b.example.com"]},
apex="example.com",
ip4="203.0.113.7",
ip6=None,
proxied=True,
explicit_domains=None,
min_child_depth=2,
ipv6_enabled=True,
)
self.assertTrue(all(r.get("proxied") is True for r in recs))
self.assertEqual(_as_set(recs), {("A", "*.b", "203.0.113.7", True)})
def test_explicit_domains_override_source(self):
cpda = {"svc": ["ignore.me.example.com", "a.b.example.com"]}
explicit = ["c.wiki.example.com"]
recs = self.wildcard_records(
current_play_domains_all=cpda,
apex="example.com",
ip4="203.0.113.5",
ip6="2606:4700:4700::1111",
proxied=False,
explicit_domains=explicit,
min_child_depth=2,
ipv6_enabled=True,
)
self.assertEqual(
_as_set(recs),
{
("A", "*.wiki", "203.0.113.5", False),
("AAAA", "*.wiki", "2606:4700:4700::1111", False),
},
)
def test_nested_structures_flattened_correctly(self):
cpda = {
"svc1": {
"primary": ["c.wiki.example.com"],
"extra": {"alt": ["a.b.example.com"]},
},
"svc2": "www.a.b.example.com",
"svc3": ["x.example.net"], # wrong apex → ignored
}
recs = self.wildcard_records(
current_play_domains_all=cpda,
apex="example.com",
ip4="203.0.113.21",
ip6="2606:4700:4700::1111",
proxied=False,
explicit_domains=None,
min_child_depth=2,
ipv6_enabled=True,
)
got = _as_set(recs)
expected = {
("A", "*.wiki", "203.0.113.21", False),
("AAAA", "*.wiki", "2606:4700:4700::1111", False),
("A", "*.b", "203.0.113.21", False),
("AAAA", "*.b", "2606:4700:4700::1111", False),
# now included because www.a.b.example.com promotes a.b.example.com as a parent
("A", "*.a.b", "203.0.113.21", False),
("AAAA", "*.a.b", "2606:4700:4700::1111", False),
}
self.assertEqual(got, expected)
def test_error_on_missing_ip4(self):
with self.assertRaises(Exception):
self.wildcard_records(
current_play_domains_all={"svc": ["a.b.example.com"]},
apex="example.com",
ip4="", # must not be empty
ip6=None,
proxied=False,
explicit_domains=None,
min_child_depth=2,
ipv6_enabled=True,
)
if __name__ == "__main__":
unittest.main()