mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-09 20:26:33 +00:00
Ansible forks a fresh worker process for every task, so the in-process
cache behind lookup('applications') was cold in every task. Each task that
touched the merged applications tree (config, domains, nginx, tls, cdn and
their callers) paid a full render: about 16 s on main and 21 to 23 s on this
branch, where ten more application roles carry MCP and AI Jinja. Measured on
the workspace jobs of run 33574056480 against the main run: 331 such tasks
(93 min, 39 % of the job) versus 391 (141 min, 43 %); the same unit is 9 to
43 % of every compose and swarm deploy job on disk. The parent process
rendered once per play, late, while templating a task name, and only the
tasks after that point ran warm.
The constructor now renders once through the new applications_carrier
action and parks the payload with its cache key in the host fact
_INFINITO_APPLICATIONS_RENDERED. Every later worker inherits the fact at
fork time and get_merged_applications serves it while the key (roles dir,
applications, users, DOMAIN_PRIMARY, SYSTEM_EMAIL_DOMAIN) still matches; a
mismatch falls back to the old render. The action masks its own result: the
payload is the whole tree with every credential, and CI runs with
MASK_CREDENTIALS_IN_LOGS false.
The task sits after the networks, design and service_provider merges, after
the add_host placements and after the token store. An independent review
showed that an earlier position bakes stale values into the play: the
web-app-wordpress title rendered to 'Blog by ' because service_provider did
not exist yet. User records inside the payload are the token store's state
at render time; readers of tokens captured later keep using lookup('users')
or lookup('config', app, 'users.…'), which read the users cache fresh.
Expected savings per job: about 130 min on a fork workspace run, 16 min on
an average compose deploy and 28 min on an average swarm deploy. The
constructor's own lookups before the carrier stay cold.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
74 lines
2.2 KiB
Python
74 lines
2.2 KiB
Python
import unittest
|
|
from unittest.mock import patch
|
|
|
|
from ansible.errors import AnsibleActionFail
|
|
|
|
from plugins.action.applications_carrier import ActionModule
|
|
from utils.cache.carrier import APPLICATIONS_RENDERED_FACT
|
|
|
|
|
|
class _FakeTask:
|
|
def __init__(self, args=None):
|
|
self.args = {} if args is None else dict(args)
|
|
|
|
|
|
class _FakeLookup:
|
|
def __init__(self, carrier):
|
|
self.carrier = carrier
|
|
self.calls = []
|
|
|
|
def run(self, terms, variables=None, **kwargs):
|
|
self.calls.append((terms, variables, kwargs))
|
|
return [self.carrier]
|
|
|
|
|
|
def _make_action(task: _FakeTask) -> ActionModule:
|
|
action = object.__new__(ActionModule)
|
|
action._task = task
|
|
action._loader = None
|
|
action._templar = None
|
|
return action
|
|
|
|
|
|
class TestApplicationsCarrierActionPlugin(unittest.TestCase):
|
|
def test_parks_the_carrier_as_a_masked_fact(self):
|
|
carrier = {"key": ["roles", ["a", "b", "c", "d"]], "applications": {"x": {}}}
|
|
lookup = _FakeLookup(carrier)
|
|
action = _make_action(_FakeTask())
|
|
with (
|
|
patch(
|
|
"plugins.action.applications_carrier.ActionBase.run",
|
|
autospec=True,
|
|
return_value={},
|
|
),
|
|
patch(
|
|
"plugins.action.applications_carrier.lookup_loader.get",
|
|
return_value=lookup,
|
|
),
|
|
):
|
|
result = action.run(task_vars={"applications": {}})
|
|
|
|
self.assertIs(result["ansible_facts"][APPLICATIONS_RENDERED_FACT], carrier)
|
|
self.assertTrue(result["_ansible_no_log"])
|
|
self.assertFalse(result["changed"])
|
|
terms, variables, kwargs = lookup.calls[0]
|
|
self.assertEqual(terms, [])
|
|
self.assertEqual(variables, {"applications": {}})
|
|
self.assertTrue(kwargs["carrier"])
|
|
|
|
def test_rejects_arguments(self):
|
|
action = _make_action(_FakeTask(args={"foo": 1}))
|
|
with (
|
|
patch(
|
|
"plugins.action.applications_carrier.ActionBase.run",
|
|
autospec=True,
|
|
return_value={},
|
|
),
|
|
self.assertRaises(AnsibleActionFail),
|
|
):
|
|
action.run(task_vars={})
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|