diff --git a/changelog/62336.fixed b/changelog/62336.fixed new file mode 100644 index 00000000000..300de31a017 --- /dev/null +++ b/changelog/62336.fixed @@ -0,0 +1 @@ +Fix pyobjects renderer access to opts and sls diff --git a/salt/renderers/pyobjects.py b/salt/renderers/pyobjects.py index c55549d9e8f..b65898d1bd5 100644 --- a/salt/renderers/pyobjects.py +++ b/salt/renderers/pyobjects.py @@ -210,6 +210,24 @@ The following pairs of lines are functionally equivalent: value = __salt__['config.get']('foo:bar:baz', 'qux') +Opts dictionary and SLS name +---------------------------- + +Pyobjects provides variable access to the minion options dictionary and the SLS +name that the code resides in. These variables are the same as the `opts` and +`sls` variables available in the Jinja renderer. + +The following lines show how to access that information. + +.. code-block:: python + :linenos: + + #!pyobjects + + test_mode = __opts__["test"] + sls_name = __sls__ + + Map Data -------- @@ -400,6 +418,8 @@ def render(template, saltenv="base", sls="", salt_data=True, **kwargs): "__salt__": __salt__, "__pillar__": __pillar__, "__grains__": __grains__, + "__opts__": __opts__, + "__sls__": sls, } ) except NameError: diff --git a/tests/pytests/unit/utils/test_pyobjects.py b/tests/pytests/unit/utils/test_pyobjects.py new file mode 100644 index 00000000000..5efd7f606da --- /dev/null +++ b/tests/pytests/unit/utils/test_pyobjects.py @@ -0,0 +1,75 @@ +import logging + +import pytest +import salt.config +import salt.renderers.pyobjects as pyobjects +from salt.utils.odict import OrderedDict +from tests.support.mock import MagicMock + +log = logging.getLogger(__name__) + + +@pytest.fixture +def cache_dir(tmp_path): + cachedir = tmp_path / "cachedir" + cachedir.mkdir() + return cachedir + + +@pytest.fixture +def minion_config(cache_dir): + opts = salt.config.DEFAULT_MINION_OPTS.copy() + opts["cachedir"] = str(cache_dir) + opts["file_client"] = "local" + opts["id"] = "testminion" + return opts + + +@pytest.fixture() +def configure_loader_modules(minion_config): + pillar = MagicMock(return_value={}) + return { + pyobjects: { + "__opts__": minion_config, + "__pillar__": pillar, + "__salt__": { + "config.get": MagicMock(), + "grains.get": MagicMock(), + "mine.get": MagicMock(), + "pillar.get": MagicMock(), + }, + }, + } + + +@pytest.fixture +def pyobjects_template(): + class Template: + def readlines(): # pylint: disable=no-method-argument + return [ + "#!pyobjects", + "state_id = __sls__ + '_' + __opts__['id']", + "File.directory(state_id, name='/tmp', mode='1777', owner='root', group='root')", + ] + + return Template + + +@pytest.mark.slow_test +def test_opts_and_sls_access(pyobjects_template): + ret = pyobjects.render(pyobjects_template, sls="pyobj.runtest") + assert ret == OrderedDict( + [ + ( + "pyobj.runtest_testminion", + { + "file.directory": [ + {"group": "root"}, + {"mode": "1777"}, + {"name": "/tmp"}, + {"owner": "root"}, + ] + }, + ), + ] + )