mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2025-09-14 06:17:14 +02:00
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:
@@ -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()
|
@@ -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()
|
Reference in New Issue
Block a user