Krikri::PythonLookupRunner
Constants
The wrapper's response kind when the controller python3 cannot
import the ansible package at all - the plugin file itself
(which imports LookupBase at its top level) could never load
either, so the whole mechanism is unavailable and the caller
keeps the old "unknown lookup" behavior.
The controller-side wrapper: imports the plugin file, finds its
LookupModule class (required to subclass the real
ansible.plugins.lookup.LookupBase, exactly like Ansible's
own loader check), instantiates it with the loader/name Ansible's
own loader would give it, and calls run(terms, variables=..., **kwargs), printing the JSON-encoded result. Always responds with
a single-line JSON object ({"ok": true, ...} / {"ok": false, kind,
error}), so a plugin that prints its own noise cannot corrupt the
protocol - only the LAST line of stdout is parsed. An ansible
package the controller python3 cannot import reports kind
"unavailable" (the whole mechanism degrades; the plugin file
could not have imported its own base class either).
Two pieces of Ansible's loader are reproduced here because real
plugins depend on them: the plugin's DOCUMENTATION options get
registered with ansible's config manager and its _load_name set
(PluginLoader._load_config_defs + the loader's own name), without
which self.set_options()/self.get_option() raises KeyError -
a very common plugin idiom; and the constructor receives a real
DataLoader, so self._loader.path_dwim_relative(...) (what
find_file_in_search_path is built on) works instead of dying on
None. _templar stays None, the one piece this wrapper cannot
fake. Both are best-effort: any failure leaves the plugin
instantiated exactly as before.
Class methods
Where the spawn-failure warning goes - STDERR in production, replaceable so a spec can capture the text without touching the process's STDERR (same pattern as PythonFilterRunner's).
Instance methods
Invokes the LookupModule from the plugin source for name with
the resolved terms/kwargs and the current task variables
(Ansible passes its own vars dict, which conventionally
carries an omit key - see expression_evaluator's call site),
returning the structured result list. Raises LookupError when the
invocation fails (callers degrade to "undefined" only when the
mechanism itself is unavailable - a dispatched failure is a real
lookup error).
Ansible's configured lookup plugin directories, searched LAST (its
loader puts the role/playbook _extra_dirs ahead of the configured
paths). See PythonFilterRunner#configured_roots for the shared
ANSIBLE_<TYPE>_PLUGINS / ANSIBLE_HOME rules.
A lookup_plugins/<name>.py source for name, searched in the
role's own lookup_plugins/ first, then the playbook-adjacent
one, then the lookup_plugins/ of every role LOADED this run
(Ansible's add_all_plugin_dirs at Role.load - a meta dependency's
lookup plugin stays reachable from the depending role's own
tasks, which is how gzm55.local_id_plugin's id served
gzm55.local_ssh_known_hosts, issue #519; same nearest-first
convention as PythonFilterRunner#find_sources), and finally
Ansible's CONFIGURED lookup directories (see configured_roots).
Unlike filters - whose names come from each FilterModule's
filters() dict - Ansible's plugin loader derives a lookup
plugin's name from its FILE NAME, so no Python introspection (and
no mtime cache) is needed here: lookup_plugins/ manala_environment_files.py IS the manala_environment_files
lookup, whatever class it declares.
community.hashi_vault's lookup is not implemented, but ansible's
loader imports hvac before the lookup runs, so on a controller
python3 without it the task fails at load time with this message
(the host whose Python is named is the controller itself). With hvac
present ansible would go on to reach a Vault server, which krikri
cannot do either, so only the missing-library case is reproducible.
The CONTROLLER's own python3 - the interpreter Ansible needs on the controller anyway (same detection pattern as PythonFilterRunner#python_executable).