applications lookup π¦ΒΆ
Contributor-facing rules for the applications.py lookup plugin. For general documentation rules such as links, writing style, RFC 2119 keywords, and Sphinx behavior, see documentation.md.
Access Pattern π―ΒΆ
The applications lookup is the ONLY supported runtime entry point for merged application configuration.
Runtime consumers (roles, playbooks, templates, filter plugins, lookup plugins, tests) MUST read application data via
lookup('applications')or a wrapper that calls it.Consumers MUST NOT materialize a top-level
applicationsfact viaansible.builtin.set_fact,combine, or any custom aggregation task.Consumers MUST NOT reintroduce
group_vars/all/05_applications.ymlor any equivalent generated dictionary file.
Lookup Forms π§ΒΆ
Form |
Returns |
|---|---|
|
Full merged mapping keyed by |
|
Merged entry for that application. Raises when missing. |
|
Merged entry, or |
|
|
Data Sources πΒΆ
The lookup merges exactly two sources:
Defaults discovered from each roleβs per-topic meta files:
roles/*/meta/server.yml,roles/*/meta/rbac.yml,roles/*/meta/services.yml,roles/*/meta/volumes.yml, plusroles/*/meta/secrets.yml(post-apply_schema()). Variants fromroles/*/meta/variants.ymldeep-merge over the assembled per-role payload. See layout.md.Overrides supplied through the normal Ansible variable
applicationsin inventory, group vars, host vars, or role vars.
No other source is consulted.
Play-Scoped Carrier π§ΒΆ
Ansible forks a fresh worker process for every task. The in-process cache inside get_merged_applications therefore never survives from one task to the next, and every task that touches the lookup would render the full tree again (about 16 to 23 seconds per task on a CI runner, measured on the workspace jobs).
The constructor stage renders once through the applications_carrier action and parks the result together with its cache key in the host fact _INFINITO_APPLICATIONS_RENDERED (the action calls lookup('applications', carrier=True)). Every later worker inherits that fact at fork time; get_merged_applications serves it whenever the key still matches and renders only when it does not (a changed applications, users, DOMAIN_PRIMARY, SYSTEM_EMAIL_DOMAIN or group_names, or a changed path or content of the tokens file).
The stage-wide task sits in
tasks/stages/01_constructor.ymlafter thenetworks,designandservice_providermerges, after theadd_hostplacements and after the token store, so the payload renders with the samegroup_names,groupsand user records a role would see. Moving it earlier bakes stale values into the play.The carrier is parked again after every role in
tasks/utils/setup/role.yml, after every nested application load intasks/utils/load/app.yml, and after every persisted token inroles/sys-token-store/tasks/write.yml. The first two run on every host of the include, also on hosts the loaded role skips. A re-park whose key still matches serves the carried payload without rendering.The action renders with
application_idremoved from its variables, so the carried tree holds the play-scope result of every consumer-alignedlookup('tls', ...), whichever role parks it.The tokens file enters the key as the SHA-256 of its content, memoised per inode, mtime and size, so a touch keeps the key. The carrier action opens the file before it computes the key, so an NFS client keys on revalidated attributes.
The action masks its own result: the fact is the whole rendered tree with every applicationβs credentials, so it never reaches a log regardless of
MASK_CREDENTIALS_IN_LOGS.User records inside the payload are the token storeβs state at render time. Readers of tokens captured later in the play MUST go through
lookup('users', ...)orlookup('config', <app>, 'users.<key>...'), both of which read the users cache fresh.The carrier is private to the lookup. Consumers MUST NOT read
_INFINITO_APPLICATIONS_RENDERED; they keep going throughlookup('applications'), which is what makes the carrier transparent.The carrier is the only materialised merged view a play MAY hold. The rule against a top-level
applicationsfact above is unchanged.
Adding an Application Entry βΒΆ
You MUST add new application defaults in the owning role:
Create or edit the appropriate
roles/<application_id>/meta/<topic>.ymlfiles (services.yml,server.yml,rbac.yml,volumes.yml,secrets.yml). See layout.md for the per-topic content.The role directory name is the
application_id. For exampleroles/web-app-mailu/meta/services.ymlis exposed asapplications['web-app-mailu'].services,roles/web-app-mailu/meta/server.ymlasapplications['web-app-mailu'].server, and so on.Keep all application-scoped defaults (services, server settings, RBAC, volumes, credentials schema) inside those role-local meta files.
The lookup discovers the entry automatically on the next run. No generator step is required.
Overriding an Application Entry π οΈΒΆ
You MUST override application values through the normal Ansible variable applications:
# group_vars/<group>.yml or host_vars/<host>.yml
applications:
web-app-mailu:
server:
domains:
canonical:
- mail.example.org
Overrides MUST use the canonical
application_idas the key.Overrides merge recursively on top of role-local defaults.
Overrides MUST NOT be written back into
roles/*/meta/*.yml; those files are for defaults only.
What Not To Do π«ΒΆ
You MUST NOT create
group_vars/all/05_applications.ymlor any similar generated applications dictionary.You MUST NOT add a make target, script, or role task whose purpose is to generate, render, merge, or rewrite such a file.
You MUST NOT introduce an alternative key source. The
application_idis always the role directory name.You MUST NOT filter applications inside the lookup. The lookup returns the full set; filtering by enabled or allowed apps stays a caller concern (for example applications_current_play.py).
Reference Files πΒΆ
File |
Purpose |
|---|---|
Runtime entry point for the |
|
Shared aggregation helper that builds and caches defaults ( |
|
The play-scoped carrier: fact name, |
|
Unit tests covering the full-dict, single-entry, override, strict missing, and non-strict missing cases. |
For the related users pattern see users.md.
For the per-role matrix-variant list that backs applications when more than one shape exists, see variants.md (deploy-time mechanism) and variants.md (file format).