mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 19:01:54 +00:00
282 lines
10 KiB
Python
282 lines
10 KiB
Python
import unittest
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
from ansible.errors import AnsibleError
|
|
|
|
from plugins.lookup.container_extra_hosts import LookupModule
|
|
|
|
ONION = "auth." + "b" * 56 + ".onion"
|
|
MAIL_ONION = "mail." + "c" * 56 + ".onion"
|
|
|
|
|
|
class TestContainerExtraHostsLookup(unittest.TestCase):
|
|
def setUp(self):
|
|
self.lookup = LookupModule()
|
|
|
|
def _get(name, *args, **kwargs):
|
|
plugin = MagicMock()
|
|
|
|
def _run(terms, variables=None, **_kwargs):
|
|
vars_ = variables or {}
|
|
if name == "config":
|
|
if "services.email.enabled" in terms:
|
|
return [vars_.get("_email_enabled", False)]
|
|
return [vars_.get("_sso_enabled", False)]
|
|
if name == "tls":
|
|
return [vars_.get("_provider_domain", "")]
|
|
if name == "email":
|
|
return [{"host": vars_.get("_mail_host", "")}]
|
|
raise AssertionError(f"unexpected lookup '{name}'")
|
|
|
|
plugin.run.side_effect = _run
|
|
return plugin
|
|
|
|
patcher = patch(
|
|
"plugins.lookup.container_extra_hosts.lookup_loader.get", side_effect=_get
|
|
)
|
|
patcher.start()
|
|
self.addCleanup(patcher.stop)
|
|
|
|
def _run(self, extra_hosts=None, docker_flags=None, **overrides):
|
|
variables = {
|
|
"application_id": "web-app-espocrm",
|
|
"_sso_enabled": True,
|
|
"_provider_domain": ONION,
|
|
"DEPLOYMENT_MODE": "compose",
|
|
}
|
|
variables.update(overrides)
|
|
kwargs = {} if extra_hosts is None else {"extra_hosts": extra_hosts}
|
|
if docker_flags is not None:
|
|
kwargs["docker_flags"] = docker_flags
|
|
return self.lookup.run(None, variables=variables, **kwargs)[0]
|
|
|
|
def test_an_onion_mail_relay_is_pinned_so_a_proxyless_client_can_reach_it(self):
|
|
"""GitLab's Net::SMTP takes no proxy, so an unpinned .onion relay
|
|
swallows every message: the send is queued and reported successful."""
|
|
block = self._run(
|
|
_sso_enabled=False, _email_enabled=True, _mail_host=MAIL_ONION
|
|
)
|
|
self.assertEqual(
|
|
block,
|
|
'extra_hosts:\n - "host.docker.internal:host-gateway"\n'
|
|
f' - "{MAIL_ONION}:host-gateway"',
|
|
)
|
|
|
|
def test_docker_flags_emit_add_host_for_a_launcher_built_container(self):
|
|
"""Discourse has no compose service -- its launcher builds the
|
|
container from docker_args, so the pins must reach it as run flags."""
|
|
block = self._run(
|
|
_sso_enabled=False,
|
|
_email_enabled=True,
|
|
_mail_host=MAIL_ONION,
|
|
docker_flags=True,
|
|
)
|
|
self.assertEqual(
|
|
block,
|
|
" - --add-host=host.docker.internal:host-gateway\n"
|
|
f" - --add-host={MAIL_ONION}:host-gateway",
|
|
)
|
|
|
|
def test_docker_flags_stay_empty_when_there_is_nothing_to_pin(self):
|
|
self.assertEqual(
|
|
self._run(_sso_enabled=False, _email_enabled=False, docker_flags=True), ""
|
|
)
|
|
|
|
def test_a_clearnet_mail_relay_is_left_alone(self):
|
|
self.assertEqual(
|
|
self._run(
|
|
_sso_enabled=False, _email_enabled=True, _mail_host="mail.example.org"
|
|
),
|
|
"",
|
|
)
|
|
|
|
def test_mail_disabled_emits_nothing(self):
|
|
self.assertEqual(
|
|
self._run(_sso_enabled=False, _email_enabled=False, _mail_host=MAIL_ONION),
|
|
"",
|
|
)
|
|
|
|
def test_swarm_pins_the_mail_relay_to_the_node_address(self):
|
|
block = self._run(
|
|
_sso_enabled=False,
|
|
_email_enabled=True,
|
|
_mail_host=MAIL_ONION,
|
|
DEPLOYMENT_MODE="swarm",
|
|
ansible_facts={"default_ipv4": {"address": "10.0.0.7"}},
|
|
)
|
|
self.assertIn(f' - "{MAIL_ONION}:10.0.0.7"', block)
|
|
|
|
def test_the_sso_and_mail_pins_coexist_without_repeating_the_alias(self):
|
|
block = self._run(_email_enabled=True, _mail_host=MAIL_ONION)
|
|
self.assertEqual(
|
|
block,
|
|
'extra_hosts:\n - "host.docker.internal:host-gateway"\n'
|
|
f' - "{ONION}:host-gateway"\n'
|
|
f' - "{MAIL_ONION}:host-gateway"',
|
|
)
|
|
|
|
def test_compose_pins_the_provider_to_the_host_gateway(self):
|
|
self.assertEqual(
|
|
self._run(),
|
|
'extra_hosts:\n - "host.docker.internal:host-gateway"\n'
|
|
f' - "{ONION}:host-gateway"',
|
|
)
|
|
|
|
def test_swarm_pins_the_provider_to_the_node_address(self):
|
|
block = self._run(
|
|
DEPLOYMENT_MODE="swarm",
|
|
ansible_facts={"default_ipv4": {"address": "10.0.0.7"}},
|
|
)
|
|
self.assertIn(f' - "{ONION}:10.0.0.7"', block)
|
|
self.assertNotIn(f"{ONION}:host-gateway", block)
|
|
|
|
def test_docker_internal_alias_is_always_present(self):
|
|
self.assertIn(' - "host.docker.internal:host-gateway"', self._run())
|
|
|
|
def test_sso_disabled_emits_nothing(self):
|
|
self.assertEqual(self._run(_sso_enabled=False), "")
|
|
|
|
def test_clearnet_provider_emits_nothing(self):
|
|
self.assertEqual(self._run(_provider_domain="auth.example.org"), "")
|
|
|
|
def test_onion_substring_that_is_not_the_suffix_emits_nothing(self):
|
|
self.assertEqual(self._run(_provider_domain="auth.onion.example.org"), "")
|
|
|
|
def test_empty_provider_domain_emits_nothing(self):
|
|
self.assertEqual(self._run(_provider_domain=""), "")
|
|
|
|
def test_compose_mode_force_overrides_the_cluster_mode(self):
|
|
block = self._run(compose_mode_force="compose", DEPLOYMENT_MODE="swarm")
|
|
self.assertIn(f' - "{ONION}:host-gateway"', block)
|
|
|
|
def test_deployment_mode_is_used_without_an_override(self):
|
|
block = self._run(
|
|
compose_mode_force="",
|
|
DEPLOYMENT_MODE="swarm",
|
|
ansible_facts={"default_ipv4": {"address": "10.0.0.8"}},
|
|
)
|
|
self.assertIn(f' - "{ONION}:10.0.0.8"', block)
|
|
|
|
def test_absent_deployment_mode_defaults_to_compose(self):
|
|
variables = {
|
|
"application_id": "web-app-espocrm",
|
|
"_sso_enabled": True,
|
|
"_provider_domain": ONION,
|
|
}
|
|
block = self.lookup.run(None, variables=variables)[0]
|
|
self.assertIn(f' - "{ONION}:host-gateway"', block)
|
|
|
|
def test_swarm_without_facts_raises(self):
|
|
with self.assertRaises(AnsibleError):
|
|
self._run(DEPLOYMENT_MODE="swarm")
|
|
|
|
def test_positional_terms_raise(self):
|
|
with self.assertRaises(AnsibleError):
|
|
self.lookup.run(["web-app-espocrm"], variables={})
|
|
|
|
def test_missing_application_id_raises(self):
|
|
with self.assertRaises(AnsibleError):
|
|
self.lookup.run(None, variables={})
|
|
|
|
def test_application_id_kwarg_overrides_the_play_var(self):
|
|
seen = []
|
|
|
|
def _get(name, *args, **kwargs):
|
|
plugin = MagicMock()
|
|
|
|
def _run(terms, variables=None, **_kwargs):
|
|
if name == "config":
|
|
seen.append(terms[0])
|
|
return [False]
|
|
return [""]
|
|
|
|
plugin.run.side_effect = _run
|
|
return plugin
|
|
|
|
with patch(
|
|
"plugins.lookup.container_extra_hosts.lookup_loader.get", side_effect=_get
|
|
):
|
|
self.lookup.run(
|
|
None,
|
|
variables={"application_id": "web-app-joomla"},
|
|
application_id="web-app-penpot",
|
|
)
|
|
self.assertEqual(set(seen), {"web-app-penpot"})
|
|
|
|
def test_caller_entries_alone_emit_a_block_without_sso(self):
|
|
self.assertEqual(
|
|
self._run(_sso_enabled=False, extra_hosts=["seaweedfs.example:1.2.3.4"]),
|
|
'extra_hosts:\n - "seaweedfs.example:1.2.3.4"',
|
|
)
|
|
|
|
def test_caller_entries_are_appended_after_the_sso_pins(self):
|
|
block = self._run(extra_hosts=["db.example:10.0.0.9"])
|
|
self.assertEqual(
|
|
block.splitlines(),
|
|
[
|
|
"extra_hosts:",
|
|
' - "host.docker.internal:host-gateway"',
|
|
f' - "{ONION}:host-gateway"',
|
|
' - "db.example:10.0.0.9"',
|
|
],
|
|
)
|
|
|
|
def test_a_bare_string_is_accepted(self):
|
|
self.assertIn(
|
|
' - "one.example:192.0.2.1"',
|
|
self._run(_sso_enabled=False, extra_hosts="one.example:192.0.2.1"),
|
|
)
|
|
|
|
def test_duplicates_collapse_in_first_seen_order(self):
|
|
block = self._run(
|
|
extra_hosts=["host.docker.internal:host-gateway", "db.example:10.0.0.9"]
|
|
)
|
|
self.assertEqual(block.count("host.docker.internal"), 1)
|
|
self.assertEqual(
|
|
block.splitlines()[1], ' - "host.docker.internal:host-gateway"'
|
|
)
|
|
|
|
def test_empty_entries_are_dropped(self):
|
|
self.assertEqual(
|
|
self._run(_sso_enabled=False, extra_hosts=["", None, " "]), ""
|
|
)
|
|
|
|
def test_entry_without_a_separator_raises(self):
|
|
with self.assertRaises(AnsibleError):
|
|
self._run(_sso_enabled=False, extra_hosts=["no-address-here"])
|
|
|
|
def test_no_sources_at_all_emit_nothing(self):
|
|
self.assertEqual(self._run(_sso_enabled=False, extra_hosts=[]), "")
|
|
|
|
def _run_entries(self, **overrides):
|
|
variables = {
|
|
"application_id": "web-app-discourse",
|
|
"_sso_enabled": True,
|
|
"_provider_domain": ONION,
|
|
"DEPLOYMENT_MODE": "compose",
|
|
}
|
|
variables.update(overrides)
|
|
return self.lookup.run(None, variables=variables, entries_only=True)
|
|
|
|
def test_entries_only_returns_the_raw_mappings(self):
|
|
self.assertEqual(
|
|
self._run_entries(),
|
|
["host.docker.internal:host-gateway", f"{ONION}:host-gateway"],
|
|
)
|
|
|
|
def test_entries_only_follows_the_swarm_address(self):
|
|
self.assertEqual(
|
|
self._run_entries(
|
|
DEPLOYMENT_MODE="swarm",
|
|
ansible_facts={"default_ipv4": {"address": "10.0.0.7"}},
|
|
),
|
|
["host.docker.internal:host-gateway", f"{ONION}:10.0.0.7"],
|
|
)
|
|
|
|
def test_entries_only_is_empty_without_sso(self):
|
|
self.assertEqual(self._run_entries(_sso_enabled=False), [])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|