cli.meta.roles.services.called package

Module contents

Verify that every role declaring required_by in its meta/services.yml actually ran in an Ansible run log.

A role’s meta/services.yml MAY contain (per-service-entity):

<entity>:
required_by:

categories: [web, web.app, …] # category handles (cf. meta/categories.yml) roles: [web-app-yourls, …] # full role names incl. category prefix

A required_by block MAY instead nest compose: / swarm: sub-blocks to scope the requirement to one deploy mode (a flat block applies in every mode); the deploy mode is inferred from the role set (swarm pulls in svc-swarm-node):

<entity>:
required_by:

compose: { categories: [web] } # required only in compose deploys

Semantics: when the current deploy contains any role whose id matches a listed roles entry, OR whose category set (top-level + first sub-level) intersects the listed categories, the role owning the services.yml entry MUST have executed (at least one of its TASK blocks must end with a non-skip status) in the run log slice that this verifier scans.

A missing event indicates a coverage regression in the deploy chain (typically: a once-flag swallowed an include before the role could run).

Two log-parsing trip-wires shape the regexes below. The non-skip status match is anchored to line start (after optional indent) so a substring in arbitrary task output cannot fake a status. And Ansible’s log_path file (ANSIBLE_LOG_PATH, what the swarm/compose deploy feeds this verifier) prefixes every line with <ts> p=<pid> u=<user> n=<name> <LVL>| `; left unstripped that prefix defeats both the line anchor and the `nTASK [ block split, so every required role would false-positive as “did not execute”.

CLI: python -m cli.meta.roles.services.called –help

cli.meta.roles.services.called.categories_of(role_id: str) → set[str]

Top-level + first-sub-level categories for the given role id.

Examples

web-app-yourls -> {“web”, “web-app”} svc-db-postgres -> {“svc”, “svc-db”} sys-ctl-hlth-csp -> {“sys”, “sys-ctl”}

cli.meta.roles.services.called.container_log_size(*, container: str, log_path: str) → int

Return the byte size of log_path inside container, or 0 if absent.

cli.meta.roles.services.called.required_role_ids(*, roles_dir: Path, deployed_role_ids: list[str]) → set[str]

Return role ids whose required_by matches the current deploy.

cli.meta.roles.services.called.verify(*, roles_dir: Path, log_path: str, deployed_role_ids: list[str], container: str | None = None, log_byte_offset: int = 0) → tuple[bool, list[str]]

Returns (ok, missing_role_ids).

When container is given, log_path is interpreted as a path INSIDE that container and read via docker exec tail. When container is None, log_path is read directly from the host filesystem.

Reads from log_byte_offset to end and asserts that every required role’s body actually executed in that slice (i.e. at least one TASK block for the role ended with a non-skip status).

An unreadable log proves coverage neither way and is reported as “missing” for every required role, so the operator notices.