mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-10 12:47:04 +00:00
perf(spot): carry the rendered users next to the applications
Ansible forks a fresh worker per task, so every lookup('users') in a forked worker re-rendered the whole user catalogue (298 users, 0.3 to 0.5 s locally and about 1 s on the runner, 1 s locally once a users password is vaulted): _MERGED_USERS_CACHE lives in the process and had no carrier fact.
applications_carrier now also parks lookup('users', carrier=True) in _INFINITO_USERS_RENDERED at the same park points, and clear drops both facts. get_merged_users serves the fact to templated reads when its key matches: the applications carrier key (roles dir, applications, users, DOMAIN_PRIMARY, SYSTEM_EMAIL_DOMAIN, group_names, token store digest) plus inventory_hostname, so a carrier a run_once park broadcasts never answers another host with its become password. A park whose users carrier still matches leaves it in place instead of returning 119 KB again, so parks cost what they cost before.
Real ansible-playbook harness on 2.20.6 and 2.21.5 with the real roles tree: users renders per users-reading task after a park 1 -> 0, task 0.56 -> 0.08 s (vaulted 1.21 -> 0.17 s); a rotated token, a rotated inventory password, a cleared carrier and another host's carrier all re-render; 32 of 32 probe digests equal HEAD's. Removing the carrier check fails three unit tests. The stalwart swarm roundtrip pass 1 and its DR drill ran green with it on all nodes, with no 401 from JMAP, msmtp or LDAP.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
5689812fbe
commit
91f29d332e
10 files changed
+460
-69
No files matched your search
+147
@@ -14,12 +14,14 @@ from __future__ import annotations
|
||||
import tempfile
|
||||
import textwrap
|
||||
import unittest
|
||||
import unittest.mock as mock
|
||||
from collections import OrderedDict
|
||||
from pathlib import Path
|
||||
|
||||
from utils.cache import _reset_cache_for_tests
|
||||
from utils.cache import base as cache_base
|
||||
from utils.cache import users as cache_users
|
||||
from utils.cache.carrier import USERS_RENDERED_FACT, merged_users_cache_key
|
||||
from utils.cache.users import store as cache_users_store
|
||||
from utils.cache.yaml import dump_yaml_str
|
||||
from utils.roles.mapping import ROLE_FILE_META_SERVICES, ROLE_FILE_META_USERS
|
||||
@@ -415,6 +417,151 @@ class TestGetMergedUsersTokenSource(unittest.TestCase):
|
||||
self.assertEqual(after["foo"]["tokens"]["web-app-foo"], "minted")
|
||||
|
||||
|
||||
class _Templar:
|
||||
def __init__(self) -> None:
|
||||
self.available_variables: dict = {}
|
||||
|
||||
def template(self, value, fail_on_undefined=False):
|
||||
return value
|
||||
|
||||
|
||||
class TestGetMergedUsersCarrierFact(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
_reset_cache_for_tests()
|
||||
|
||||
@staticmethod
|
||||
def _variables(host: str = "h1") -> dict:
|
||||
return {
|
||||
"inventory_hostname": host,
|
||||
"users": {"foo": {"email": "foo@example.org"}},
|
||||
cache_users_store.STORE_FACT: {
|
||||
"foo": {"tokens": {"web-app-foo": "minted"}}
|
||||
},
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _park(roles: Path, variables: dict, payload: dict) -> dict:
|
||||
key = merged_users_cache_key(variables, roles_dir=roles)
|
||||
variables[USERS_RENDERED_FACT] = {
|
||||
"key": [key[0], list(key[1])],
|
||||
"users": payload,
|
||||
}
|
||||
return payload
|
||||
|
||||
@staticmethod
|
||||
def _merged(roles: Path, variables: dict, templar=None) -> tuple:
|
||||
with mock.patch.object(
|
||||
cache_users,
|
||||
"_render_with_templar",
|
||||
side_effect=lambda value, **_: {"rendered": value},
|
||||
) as render:
|
||||
merged = cache_users.get_merged_users(
|
||||
variables=variables,
|
||||
roles_dir=roles,
|
||||
templar=_Templar() if templar is None else templar,
|
||||
)
|
||||
return merged, render.call_count
|
||||
|
||||
def test_matching_key_serves_the_carried_payload_without_rendering(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
roles = _seed_minimal_user_role(Path(tmp))
|
||||
variables = self._variables()
|
||||
carried = self._park(roles, variables, {"foo": {"carried": True}})
|
||||
merged, renders = self._merged(roles, variables)
|
||||
self.assertIs(merged, carried)
|
||||
self.assertEqual(renders, 0)
|
||||
|
||||
def test_the_carried_payload_answers_the_rest_of_the_process(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
roles = _seed_minimal_user_role(Path(tmp))
|
||||
variables = self._variables()
|
||||
carried = self._park(roles, variables, {"foo": {"carried": True}})
|
||||
self._merged(roles, variables)
|
||||
del variables[USERS_RENDERED_FACT]
|
||||
merged, renders = self._merged(roles, variables)
|
||||
self.assertIs(merged, carried)
|
||||
self.assertEqual(renders, 0)
|
||||
|
||||
def test_stale_key_falls_back_to_a_fresh_render(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
roles = _seed_minimal_user_role(Path(tmp))
|
||||
variables = self._variables()
|
||||
key = merged_users_cache_key(variables, roles_dir=roles)
|
||||
variables[USERS_RENDERED_FACT] = {
|
||||
"key": [key[0], ["stale"] * len(key[1])],
|
||||
"users": {"foo": {"carried": True}},
|
||||
}
|
||||
merged, renders = self._merged(roles, variables)
|
||||
self.assertEqual(renders, 1)
|
||||
self.assertEqual(merged["rendered"]["foo"]["email"], "foo@example.org")
|
||||
|
||||
def test_another_hosts_carrier_is_rejected(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
roles = _seed_minimal_user_role(Path(tmp))
|
||||
variables = self._variables("h1")
|
||||
self._park(roles, variables, {"foo": {"carried": True}})
|
||||
variables["inventory_hostname"] = "h2"
|
||||
merged, renders = self._merged(roles, variables)
|
||||
self.assertEqual(renders, 1)
|
||||
self.assertNotIn("carried", merged["rendered"]["foo"])
|
||||
|
||||
def test_a_rotated_store_token_rejects_the_carrier(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
roles = _seed_minimal_user_role(Path(tmp))
|
||||
variables = self._variables()
|
||||
self._park(roles, variables, {"foo": {"carried": True}})
|
||||
variables[cache_users_store.STORE_FACT] = {
|
||||
"foo": {"tokens": {"web-app-foo": "rotated"}}
|
||||
}
|
||||
merged, renders = self._merged(roles, variables)
|
||||
self.assertEqual(renders, 1)
|
||||
self.assertEqual(
|
||||
merged["rendered"]["foo"]["tokens"]["web-app-foo"], "rotated"
|
||||
)
|
||||
|
||||
def test_a_rotated_inventory_password_rejects_the_carrier(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
roles = _seed_minimal_user_role(Path(tmp))
|
||||
variables = self._variables()
|
||||
variables["users"]["foo"]["password"] = "before"
|
||||
self._park(roles, variables, {"foo": {"carried": True}})
|
||||
variables["users"] = {"foo": {"password": "after"}}
|
||||
merged, renders = self._merged(roles, variables)
|
||||
self.assertEqual(renders, 1)
|
||||
self.assertEqual(merged["rendered"]["foo"]["password"], "after")
|
||||
|
||||
def test_without_a_templar_the_carrier_is_ignored(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
roles = _seed_minimal_user_role(Path(tmp))
|
||||
variables = self._variables()
|
||||
self._park(roles, variables, {"foo": {"carried": True}})
|
||||
merged = cache_users.get_merged_users(
|
||||
variables=variables, roles_dir=roles, templar=None
|
||||
)
|
||||
self.assertNotIn("carried", merged["foo"])
|
||||
self.assertEqual(merged["foo"]["email"], "foo@example.org")
|
||||
|
||||
def test_malformed_carrier_is_ignored(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
roles = _seed_minimal_user_role(Path(tmp))
|
||||
key = merged_users_cache_key(self._variables(), roles_dir=roles)
|
||||
for carrier in (
|
||||
"not-a-mapping",
|
||||
{"key": "x", "users": {}},
|
||||
{"key": [key[0], list(key[1])], "users": "not-a-mapping"},
|
||||
{},
|
||||
):
|
||||
with self.subTest(carrier=carrier):
|
||||
_reset_cache_for_tests()
|
||||
variables = self._variables()
|
||||
variables[USERS_RENDERED_FACT] = carrier
|
||||
merged, renders = self._merged(roles, variables)
|
||||
self.assertEqual(renders, 1)
|
||||
self.assertEqual(
|
||||
merged["rendered"]["foo"]["email"], "foo@example.org"
|
||||
)
|
||||
|
||||
|
||||
class TestStoreSignature(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
_reset_cache_for_tests()
|
||||
|
||||
Reference in new issue
Block a user