mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-12 21:56:21 +00:00
Merge branch 'feature/updater' into feature/store-i18n
16 conflicts, resolved per hunk: - sys-stk-front-proxy: tasks/main.yml keeps the network-sibling loop; the single stack_host_contents write of http.conf plus vhost and the front_proxy_vhost_changed gate move into tasks/vhost.yml, with the http.conf decided per sibling domain so an onion vhost gets no HTTPS redirect. - sys-front-tls: 01_http.yml is gone as the updater intends; the cert-renew include in main.yml gates on the vhost domain's TLS. - sys-svc-compose: the updater's resolve_host_cid without the remote echo and the prepared pending flag in up.yml, written with IS_SWARM_MODE and the repeated-when block grouping. - web-app-nextcloud: the updater's post-upgrade gate and pending-only plugin configuration, which already carry the pinned-plugin handling. - web-app-xwiki: one XWIKI_CORE_VERSION for the readiness probe and style.css.j2; OIDC/LDAP follow xwiki_extensions_ready at bootstrap, the S3 blob store stays off there as TestBlobstoreRenderOrder demands. - roles_with_service: onion swap and dashboard card key combined, the canonical domain is the canonical URL's host in both branches. - sys-ctl-hlth-csp: --clearnet-timeout and --concurrency together; run_checker forwards its options to build_docker_cmd, keeping the script under the 500-line limit. - web-app-matrix: Element branding and web_client_location side by side. - web-app-stalwart/README.md: the generated Cosmos section is dropped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
99123762df
72 files changed
+2844
-345
No files matched your search
@@ -125,6 +125,26 @@ class DummyTlsResolveLookup:
|
||||
return [base_url]
|
||||
|
||||
|
||||
class DummyCanonicalUrlLookup:
|
||||
"""
|
||||
Deterministic canonical_url stub.
|
||||
Emulates: lookup('canonical_url', app_id, key) -> 'http(s)://<domains[app_id][key]>'
|
||||
"""
|
||||
|
||||
def run(self, terms, variables=None, **kwargs):
|
||||
variables = variables or {}
|
||||
app_id, key = terms
|
||||
app_domains = variables.get("domains", {}).get(app_id)
|
||||
if not isinstance(app_domains, dict) or key not in app_domains:
|
||||
raise AnsibleError(
|
||||
f"dummy canonical_url: '{app_id}' has no canonical '{key}'"
|
||||
)
|
||||
scheme = (
|
||||
"https" if _ansible_bool(variables.get("TLS_ENABLED", True)) else "http"
|
||||
)
|
||||
return [f"{scheme}://{app_domains[key]}"]
|
||||
|
||||
|
||||
class TestDockerCardsLookup(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.test_roles_dir = tempfile.mkdtemp(prefix="test_roles_")
|
||||
@@ -174,6 +194,8 @@ logo:
|
||||
return _VarsLookup("domains")
|
||||
if name == "tls":
|
||||
return DummyTlsResolveLookup(templar)
|
||||
if name == "canonical_url":
|
||||
return DummyCanonicalUrlLookup()
|
||||
raise AnsibleError(f"Unexpected lookup requested: {name}")
|
||||
|
||||
docker_cards_module.lookup_loader.get = _patched_get
|
||||
@@ -304,6 +326,39 @@ logo:
|
||||
|
||||
self.assertEqual(result[0][0]["url"], "http://myportfolio.com")
|
||||
|
||||
def _card_target_variables(self, dashboard):
|
||||
fake_variables = self._base_fake_variables()
|
||||
fake_variables["domains"] = {
|
||||
"portfolio": {
|
||||
"www": "myportfolio.com",
|
||||
"client": "client.myportfolio.com",
|
||||
}
|
||||
}
|
||||
fake_variables["applications"]["portfolio"]["services"]["dashboard"] = dashboard
|
||||
return fake_variables
|
||||
|
||||
def test_card_url_targets_the_card_key(self):
|
||||
fake_variables = self._card_target_variables(
|
||||
{"enabled": True, "card": "client"}
|
||||
)
|
||||
|
||||
result = self._run_lookup(LookupModule(), fake_variables)
|
||||
|
||||
self.assertEqual(result[0][0]["url"], "https://client.myportfolio.com")
|
||||
|
||||
def test_card_url_without_card_key_targets_the_primary_domain(self):
|
||||
fake_variables = self._card_target_variables({"enabled": True})
|
||||
|
||||
result = self._run_lookup(LookupModule(), fake_variables)
|
||||
|
||||
self.assertEqual(result[0][0]["url"], "https://myportfolio.com")
|
||||
|
||||
def test_unknown_card_key_fails_loudly(self):
|
||||
fake_variables = self._card_target_variables({"enabled": True, "card": "nope"})
|
||||
|
||||
with self.assertRaisesRegex(AnsibleError, "nope"):
|
||||
self._run_lookup(LookupModule(), fake_variables)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -72,13 +72,15 @@ class RolesWithServiceLookupTests(unittest.TestCase):
|
||||
handover: frozenset = frozenset(),
|
||||
**kwargs,
|
||||
):
|
||||
"""Run the lookup with the ``applications``, ``tls`` and
|
||||
``network_handover`` sub-lookups patched to stay hermetic. The stubbed
|
||||
tls answers ``[role_id, "url.base"]`` with the role's own configured
|
||||
domain, which is what a clearnet deployment resolves; pass ``url_host``
|
||||
(every role) or ``url_hosts`` (per role) to model a deployment whose URL
|
||||
host differs, such as an onion one. A role in ``handover`` hands every
|
||||
sibling outside its canonical network over."""
|
||||
"""Run the lookup with the ``applications``, ``tls``,
|
||||
``canonical_url`` and ``network_handover`` sub-lookups patched to stay
|
||||
hermetic; any other sub-lookup raises ``KeyError``. The stubbed tls
|
||||
answers ``[role_id, "url.base"]`` and the stubbed canonical_url
|
||||
``[role_id, key]`` with the role's own configured domain, which is what
|
||||
a clearnet deployment resolves; pass ``url_host`` (every role) or
|
||||
``url_hosts`` (per role) to model a deployment whose URL host differs,
|
||||
such as an onion one. A role in ``handover`` hands every sibling outside
|
||||
its canonical network over."""
|
||||
lookup = self._make_lookup(vars_ or {})
|
||||
resolve = self.mod._resolve_canonical_domain
|
||||
|
||||
@@ -100,16 +102,31 @@ class RolesWithServiceLookupTests(unittest.TestCase):
|
||||
role in handover and network_of(domain) != network_of(canonical)
|
||||
]
|
||||
|
||||
named = self.mod._resolve_named_canonical_domain
|
||||
|
||||
class _StubCanonicalUrl:
|
||||
def run(self, terms_, variables=None, **kwargs):
|
||||
role, key = str(terms_[0]), str(terms_[1])
|
||||
host = (
|
||||
(url_hosts or {}).get(role)
|
||||
or url_host
|
||||
or named(role, applications.get(role, {}), key)
|
||||
)
|
||||
return [f"https://{host}"]
|
||||
|
||||
class _StubApplications:
|
||||
def run(self, terms_, variables=None, **kwargs):
|
||||
return [applications]
|
||||
|
||||
stubs = {
|
||||
"applications": _StubApplications(),
|
||||
"tls": _StubTls(),
|
||||
"canonical_url": _StubCanonicalUrl(),
|
||||
"network_handover": _StubHandover(),
|
||||
}
|
||||
|
||||
def _dispatch(name, **_kwargs):
|
||||
if name == "applications":
|
||||
return _StubApplications()
|
||||
if name == "network_handover":
|
||||
return _StubHandover()
|
||||
return _StubTls()
|
||||
return stubs[name]
|
||||
|
||||
with patch.object(self.mod.lookup_loader, "get", side_effect=_dispatch):
|
||||
return lookup.run(terms, variables=vars_ or {}, **kwargs)
|
||||
@@ -683,6 +700,60 @@ class RolesWithServiceLookupTests(unittest.TestCase):
|
||||
with self.assertRaises(AnsibleError):
|
||||
self._network_ids({}, network="onion")
|
||||
|
||||
def _card_target_role(self, service: str, **block_extra) -> dict:
|
||||
block = {"enabled": True, "shared": True}
|
||||
block.update(block_extra)
|
||||
return {
|
||||
"web-app-chat": {
|
||||
"services": {service: block},
|
||||
"domains": {
|
||||
"canonical": {
|
||||
"synapse": "matrix.example.com",
|
||||
"element": "element.example.com",
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
def test_dashboard_card_key_targets_that_domain(self):
|
||||
applications = self._card_target_role("dashboard", card="element")
|
||||
entry = self._run(["dashboard"], applications)[0][0]
|
||||
self.assertEqual(
|
||||
(entry["canonical_domain"], entry["canonical_url"]),
|
||||
("element.example.com", "https://element.example.com"),
|
||||
)
|
||||
|
||||
def test_dashboard_card_domain_follows_the_onion_url(self):
|
||||
applications = self._card_target_role("dashboard", card="element")
|
||||
entry = self._run(
|
||||
["dashboard"], applications, url_hosts={"web-app-chat": f"element.{ONION}"}
|
||||
)[0][0]
|
||||
self.assertEqual(
|
||||
(entry["canonical_domain"], entry["canonical_url"]),
|
||||
(f"element.{ONION}", f"https://element.{ONION}"),
|
||||
)
|
||||
|
||||
def test_dashboard_without_card_key_targets_the_primary_domain(self):
|
||||
applications = self._card_target_role("dashboard")
|
||||
entry = self._run(["dashboard"], applications)[0][0]
|
||||
self.assertEqual(
|
||||
(entry["canonical_domain"], entry["canonical_url"]),
|
||||
("matrix.example.com", "https://matrix.example.com"),
|
||||
)
|
||||
|
||||
def test_dashboard_unknown_card_key_fails_loudly(self):
|
||||
applications = self._card_target_role("dashboard", card="nope")
|
||||
with self.assertRaisesRegex(AnsibleError, "nope"):
|
||||
self._run(["dashboard"], applications)
|
||||
|
||||
def test_other_services_ignore_a_card_key(self):
|
||||
applications = self._card_target_role("prometheus", card="element")
|
||||
entry = self._run(["prometheus"], applications)[0][0]
|
||||
self.assertEqual(
|
||||
(entry["canonical_domain"], entry["canonical_url"]),
|
||||
("matrix.example.com", "https://matrix.example.com"),
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user