mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-13 06:06:38 +00:00
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>
75 lines
2.3 KiB
Python
75 lines
2.3 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from ansible.errors import AnsibleError
|
|
from ansible.plugins.lookup import LookupBase
|
|
|
|
from utils.cache import _reset_cache_for_tests as _reset_runtime_lookup_cache
|
|
from utils.cache.carrier import merged_users_cache_key
|
|
|
|
# nocheck: lookup-cache-import (this lookup IS the users SPOT provider)
|
|
from utils.cache.users import get_merged_users
|
|
|
|
|
|
def _reset_cache_for_tests() -> None:
|
|
_reset_runtime_lookup_cache()
|
|
|
|
|
|
class LookupModule(LookupBase):
|
|
def run(
|
|
self,
|
|
terms: list[Any] | None,
|
|
variables: dict[str, Any] | None = None,
|
|
**kwargs: Any,
|
|
) -> list[Any]:
|
|
"""
|
|
Terms:
|
|
0) full merged mapping
|
|
1) canonical_user_key
|
|
2) canonical_user_key, default value
|
|
|
|
Keyword arguments:
|
|
roles_dir: roles tree to build from (defaults to the repository's)
|
|
carrier: with no terms, return ``{"key": [...], "users": {...}}``
|
|
for the constructor to park in `_INFINITO_USERS_RENDERED`
|
|
"""
|
|
terms = terms or []
|
|
if len(terms) > 2:
|
|
raise AnsibleError(
|
|
"users: expected 0, 1, or 2 terms: "
|
|
"lookup('users'[, canonical_user_key[, default]])"
|
|
)
|
|
carrier = bool(kwargs.get("carrier", False))
|
|
if carrier and terms:
|
|
raise AnsibleError("users: carrier=True takes no terms")
|
|
|
|
variables = variables or getattr(self._templar, "available_variables", {}) or {}
|
|
roles_dir = kwargs.get("roles_dir")
|
|
users = get_merged_users(
|
|
variables=variables,
|
|
roles_dir=roles_dir,
|
|
templar=getattr(self, "_templar", None),
|
|
)
|
|
|
|
if carrier:
|
|
key = merged_users_cache_key(variables, roles_dir=roles_dir)
|
|
return [{"key": [key[0], list(key[1])], "users": users}]
|
|
|
|
if len(terms) == 0:
|
|
return [users]
|
|
|
|
user_key = str(terms[0]).strip()
|
|
default_provided = len(terms) == 2
|
|
default_value = terms[1] if default_provided else None
|
|
|
|
if user_key in users:
|
|
return [users[user_key]]
|
|
|
|
if default_provided:
|
|
return [default_value]
|
|
|
|
raise AnsibleError(
|
|
f"users: user '{user_key}' not found. Known user keys: {sorted(users)}"
|
|
)
|