From 68715c2a161b5323dd5165ba916b5fa0f3714d27 Mon Sep 17 00:00:00 2001 From: "Daniel A. Wozniak" Date: Sun, 28 Jun 2026 21:11:52 -0700 Subject: [PATCH] Fix salt-ssh Jinja TemplateNotFound on map.jinja imports Backport of 481bd7a6a72 (3007.x/3008.x) to 3006.x. SaltCacheLoader built its searchpath from opts["cachedir"]. Under salt-ssh, opts["cachedir"] points at the thin minion's remote path on the SSH target, while the master-side fileclient caches requested files under opts["_caller_cachedir"] (the master's cachedir). A Jinja import like {% from "formula/map.jinja" import x with context %} therefore raised TemplateNotFound: the file existed in the master cache, but the loader looked in the (empty) thin path. Prefer opts["_caller_cachedir"] when present so Jinja imports resolve against the master's cache dir. opts["_caller_cachedir"] is already populated on 3006.x by salt/client/ssh/__init__.py; only the consumer was missing. Only the salt/utils/jinja.py hunk from 481bd7a6a72 is backported; the test-timeout hunks in that commit are 3007.x/3008.x-only. Fixes: #31531 --- changelog/31531.fixed.md | 1 + salt/utils/jinja.py | 7 ++++++- .../unit/utils/jinja/test_salt_cache_loader.py | 18 ++++++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 changelog/31531.fixed.md diff --git a/changelog/31531.fixed.md b/changelog/31531.fixed.md new file mode 100644 index 000000000000..5fb3808a83c8 --- /dev/null +++ b/changelog/31531.fixed.md @@ -0,0 +1 @@ +Fixed ``salt-ssh`` ``TemplateNotFound`` when a managed Jinja template imports from another template (e.g. ``{% from "formula/map.jinja" import x with context %}``). ``SaltCacheLoader`` now prefers ``opts["_caller_cachedir"]`` (the master's cachedir, where the master-side fileclient caches requested files) over ``opts["cachedir"]`` (the thin minion's remote path) for its Jinja search path. Backport of the 3007.x/3008.x fix. diff --git a/salt/utils/jinja.py b/salt/utils/jinja.py index 0bda8d3ea704..384a69b2f462 100644 --- a/salt/utils/jinja.py +++ b/salt/utils/jinja.py @@ -75,7 +75,12 @@ def __init__( else: self.searchpath = opts["pillar_roots"][saltenv] else: - self.searchpath = [os.path.join(opts["cachedir"], "files", saltenv)] + # In salt-ssh context, _caller_cachedir is the master's cachedir + # while cachedir points to the thin minion's remote path. + # The fileclient caches files to the master's cachedir, so we + # must use _caller_cachedir as the Jinja search path when present. + effective_cachedir = opts.get("_caller_cachedir", opts["cachedir"]) + self.searchpath = [os.path.join(effective_cachedir, "files", saltenv)] log.debug("Jinja search path: %s", self.searchpath) self.cached = [] self._file_client = _file_client diff --git a/tests/pytests/unit/utils/jinja/test_salt_cache_loader.py b/tests/pytests/unit/utils/jinja/test_salt_cache_loader.py index be68660bccf2..1875265a9f82 100644 --- a/tests/pytests/unit/utils/jinja/test_salt_cache_loader.py +++ b/tests/pytests/unit/utils/jinja/test_salt_cache_loader.py @@ -150,6 +150,24 @@ def test_searchpath_bad_pillar_rend(minion_opts, get_loader): assert loader.searchpath == [] +def test_searchpath_uses_caller_cachedir(minion_opts, get_loader, tmp_path): + """ + When opts["_caller_cachedir"] is set (salt-ssh master-side wrapper + context), the searchpath must be built from it instead of opts["cachedir"]. + + Regression test for #31531: under salt-ssh, opts["cachedir"] points at + the thin minion's remote path while the master-side fileclient caches + files under opts["_caller_cachedir"]. Using opts["cachedir"] for the + Jinja searchpath caused TemplateNotFound on map.jinja imports. + """ + saltenv = "base" + master_cachedir = tmp_path / "master" + master_cachedir.mkdir() + minion_opts["_caller_cachedir"] = str(master_cachedir) + loader = get_loader(opts=minion_opts, saltenv=saltenv) + assert loader.searchpath == [os.path.join(str(master_cachedir), "files", saltenv)] + + def test_mockclient(minion_opts, template_dir, hello_simple, get_loader): """ A MockFileClient is used that records all file requests normally sent