mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-08 19:56:53 +00:00
The lookup takes the role from its term and falls back to reading application_id out of the play's variables. That read was raw, which is the trap that took down three jobs from webserver_resource, so it now goes through resolve_var like every other lookup. Prevention rather than a fix. The fallback is the else of a term that the only caller always passes, and where it would fire web-app-docs sets application_id literally; the roles carrying a templated id reach it through sys-stk-full, which web-app-docs does not. The consequence would also have differed: an unrendered string never matches a peer, so the replay would have asked the site for a role named after the template and failed on the page rather than in the lookup. Measured: with the resolver's body stripped back to the raw read this test and the webserver_resource one both fail, and both pass with it restored. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
81 lines
2.7 KiB
Python
81 lines
2.7 KiB
Python
"""Which deployed role's published guide a replay should read.
|
|
|
|
``lookup('guide_role', application_id)`` names exactly one role, never a list:
|
|
the replay brings up a machine per role, so a round that deployed twenty of
|
|
them still replays one guide.
|
|
|
|
The role comes from ``APP_ID``, which the deploy workflow sets to the row's
|
|
app and :mod:`cli.administration.deploy.development.deploy.run` forwards into
|
|
the container. A round that carries no such name, or one naming a role that
|
|
publishes no guide, draws an invokable peer at random instead, so a local
|
|
round covers a different guide each time rather than always the first one the
|
|
inventory happens to list. Invokability comes from
|
|
:func:`utils.roles.validation.invokable.list_invokable_app_ids`, so no caller
|
|
re-derives it from role-name prefixes.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import secrets
|
|
from typing import Any
|
|
|
|
from ansible.plugins.lookup import LookupBase
|
|
|
|
from plugins.lookup.deployment import running_apps
|
|
from utils.roles.validation.invokable import list_invokable_app_ids
|
|
from utils.templating.vars import resolve_var
|
|
|
|
ENV_VAR = "APP_ID"
|
|
|
|
|
|
def guide_role(
|
|
application_id: str,
|
|
running: list[str],
|
|
invokable: list[str],
|
|
pinned: str = "",
|
|
) -> str:
|
|
"""Return the app whose guide is replayed, ``application_id`` when it is alone.
|
|
|
|
Args:
|
|
application_id: the role asking, which is skipped as its own peer.
|
|
running: the apps this round deploys.
|
|
invokable: every app that publishes a guide.
|
|
pinned: the app the round was started for, used when it publishes a
|
|
guide and ignored when it does not, because ``APP_ID`` is set on
|
|
every deploy row and not every row deploys a documented role.
|
|
"""
|
|
known = set(invokable)
|
|
if pinned in known:
|
|
return pinned
|
|
peers = [app for app in running if app != application_id and app in known]
|
|
return secrets.choice(peers) if peers else application_id
|
|
|
|
|
|
class LookupModule(LookupBase):
|
|
def run(
|
|
self,
|
|
terms: list[Any],
|
|
variables: dict[str, Any] | None = None,
|
|
**kwargs: Any,
|
|
) -> list[str]:
|
|
vars_ = variables or getattr(self._templar, "available_variables", {}) or {}
|
|
application_id = (
|
|
str(terms[0])
|
|
if terms
|
|
else str(
|
|
resolve_var(
|
|
getattr(self, "_templar", None), vars_.get("application_id")
|
|
)
|
|
or ""
|
|
)
|
|
)
|
|
return [
|
|
guide_role(
|
|
application_id,
|
|
running_apps(vars_),
|
|
list_invokable_app_ids(),
|
|
os.environ.get(ENV_VAR, "").strip(),
|
|
)
|
|
]
|