mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-08 11:46:35 +00:00
Add group_vars/all/18_api.yml defining the global API mapping (cloudflare, telegram, github, google, jira) and plugins/lookup/api.py — the single sanctioned reader that resolves a dotted 'provider.key' path against it and raises on unknown keys. Routing every proprietary/external credential access through one lookup keeps a single point to change how credentials are sourced. Includes the unit test covering nested resolution, subtree access, error paths, and templar handling. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
49 lines
1.8 KiB
Python
49 lines
1.8 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from ansible.errors import AnsibleError
|
|
from ansible.plugins.lookup import LookupBase
|
|
|
|
|
|
class LookupModule(LookupBase):
|
|
"""
|
|
lookup('api', '<provider>.<key>') e.g. lookup('api', 'github.client_id')
|
|
|
|
Single point of truth for proprietary / external API credentials. Resolves a
|
|
dotted path against the global ``API`` mapping (group_vars/all/18_api.yml).
|
|
Routing every access through this plugin keeps one place to change how
|
|
proprietary API credentials are sourced (vault, env, secret manager, a
|
|
different provider abstraction) without touching the call sites.
|
|
"""
|
|
|
|
def run(self, terms, variables: dict[str, Any] | None = None, **kwargs):
|
|
if not terms or len(terms) != 1:
|
|
raise AnsibleError(
|
|
"lookup('api') takes exactly one term: '<provider>.<key>' "
|
|
"(e.g. 'github.client_id')."
|
|
)
|
|
|
|
templar = getattr(self, "_templar", None)
|
|
variables = variables or getattr(templar, "available_variables", {}) or {}
|
|
|
|
path = str(terms[0]).strip()
|
|
if not path:
|
|
raise AnsibleError("lookup('api'): empty key path.")
|
|
|
|
api = variables.get("API", {})
|
|
if templar is not None:
|
|
api = templar.template(api)
|
|
if not isinstance(api, dict):
|
|
raise AnsibleError("lookup('api'): global 'API' is not a mapping.")
|
|
|
|
node: Any = api
|
|
for part in path.split("."):
|
|
if not isinstance(node, dict) or part not in node:
|
|
raise AnsibleError(
|
|
f"lookup('api'): unknown key '{path}' (failed at '{part}'). "
|
|
f"Declare it under 'API' in group_vars/all/18_api.yml."
|
|
)
|
|
node = node[part]
|
|
return [node]
|