What happened?
On a minion whose execution-module dictionary (__salt__) is built by
salt.loader.resource_modules() — the resource-managed minion path on the
development/master branch (Salt Resources) — any call routed through
salt.modules.cp._client() fails. That includes cp.cache_file and therefore
every salt:// fetch a state performs (e.g. a file.managed with a
salt:// source, pkg.installed from a cached package, etc.). The failure is:
KeyError: '__file_client__'
surfaced as a salt.exceptions.LoaderError.
Root cause
cp._client() is:
def _client():
if __file_client__:
return __file_client__.value()
return salt.fileclient.get_file_client(__opts__.value())
The resource_modules loader does not pack a __file_client__ named context.
Because __file_client__ is a loader-managed context variable, evaluating it
(if __file_client__:) when the key was never packed raises LoaderError
instead of returning a falsey value. That raise short-circuits the intended
fallback, so the function never reaches get_file_client(__opts__...).
minion_mods()-built __salt__ is unaffected because that loader packs
__file_client__ (see "Populate __file_client__ for states" / "Add
__file_client__ for execution modules"). Only loaders that omit the pack
(such as resource_modules) hit this.
Steps to reproduce
- Build
__salt__ via salt.loader.resource_modules(...) without passing a
file client (so __file_client__ is not packed).
- From a state — or directly — call anything that goes through
cp._client(), e.g. cp.cache_file('salt://foo').
- Observe
KeyError: '__file_client__' / LoaderError; the __opts__
fallback never runs.
Expected behavior
cp._client() should fall back to constructing a file client from __opts__
when __file_client__ is missing/None, instead of raising.
Suggested fix
Guard the context lookup so a missing/None context falls back cleanly:
try:
file_client = __file_client__.value()
except salt.exceptions.LoaderError:
file_client = None
if file_client:
return file_client
return salt.fileclient.get_file_client(__opts__.value())
and/or have resource_modules() pack __file_client__ the same way
minion_mods() does (thread a file_client= param through the loader and
pass a shared file client from state.py).
Type of salt install
pip (git)
Major version
master / development (Salt Resources architecture; 3008)
What supported OS are you seeing the problem on?
photon-5 (architecture-independent — it is a loader/context bug, not OS-specific)
salt --versions-report output
Observed on the development (master) branch where salt.loader.resource_modules()
exists. The bug is in salt/modules/cp.py::_client() and the resource_modules
loader context packing; it is independent of platform and versions-report.
What happened?
On a minion whose execution-module dictionary (
__salt__) is built bysalt.loader.resource_modules()— the resource-managed minion path on thedevelopment/
masterbranch (Salt Resources) — any call routed throughsalt.modules.cp._client()fails. That includescp.cache_fileand thereforeevery
salt://fetch a state performs (e.g. afile.managedwith asalt://source,pkg.installedfrom a cached package, etc.). The failure is:surfaced as a
salt.exceptions.LoaderError.Root cause
cp._client()is:The
resource_modulesloader does not pack a__file_client__named context.Because
__file_client__is a loader-managed context variable, evaluating it(
if __file_client__:) when the key was never packed raisesLoaderErrorinstead of returning a falsey value. That raise short-circuits the intended
fallback, so the function never reaches
get_file_client(__opts__...).minion_mods()-built__salt__is unaffected because that loader packs__file_client__(see "Populate__file_client__for states" / "Add__file_client__for execution modules"). Only loaders that omit the pack(such as
resource_modules) hit this.Steps to reproduce
__salt__viasalt.loader.resource_modules(...)without passing afile client (so
__file_client__is not packed).cp._client(), e.g.cp.cache_file('salt://foo').KeyError: '__file_client__'/LoaderError; the__opts__fallback never runs.
Expected behavior
cp._client()should fall back to constructing a file client from__opts__when
__file_client__is missing/None, instead of raising.Suggested fix
Guard the context lookup so a missing/None context falls back cleanly:
and/or have
resource_modules()pack__file_client__the same wayminion_mods()does (thread afile_client=param through the loader andpass a shared file client from
state.py).Type of salt install
pip (git)
Major version
master / development (Salt Resources architecture; 3008)
What supported OS are you seeing the problem on?
photon-5 (architecture-independent — it is a loader/context bug, not OS-specific)
salt --versions-report output