diff --git a/changelog/58969.fixed.md b/changelog/58969.fixed.md new file mode 100644 index 000000000000..366607bd97e7 --- /dev/null +++ b/changelog/58969.fixed.md @@ -0,0 +1,4 @@ +Issue 58969: Fixes an issue with `saltclass.expand_classes_in_order` +function where it was losing nested classes states during class +expansion. The logic now use `salt.utils.odict.OrderedDict` to keep +the inclusion ordering. diff --git a/changelog/63933.fixed.md b/changelog/63933.fixed.md new file mode 100644 index 000000000000..794c170bd047 --- /dev/null +++ b/changelog/63933.fixed.md @@ -0,0 +1,4 @@ +Issue 63933: Fixes an issue with `saltclass.expanded_dict_from_minion` +function where it was passing a reference to minion `dict` which was +overridden by nested classes during class expansion. Copy the node +definition with `copy.deepcopy` instead of passing a reference. diff --git a/changelog/66252.fixed.md b/changelog/66252.fixed.md new file mode 100644 index 000000000000..2227c8998448 --- /dev/null +++ b/changelog/66252.fixed.md @@ -0,0 +1 @@ +Applying `selinux.fcontext_policy_present` to a shorter path than an existing entry now works diff --git a/changelog/66883.fixed.md b/changelog/66883.fixed.md new file mode 100644 index 000000000000..999b049857b6 --- /dev/null +++ b/changelog/66883.fixed.md @@ -0,0 +1 @@ +Added cryptogrpahy back to base.txt requirements as a dependency diff --git a/changelog/66891.fixed.md b/changelog/66891.fixed.md new file mode 100644 index 000000000000..7bd756d7f298 --- /dev/null +++ b/changelog/66891.fixed.md @@ -0,0 +1 @@ +Remove "perms" from `linux_acl.list_absent()` documentation diff --git a/changelog/66959.fixed.md b/changelog/66959.fixed.md new file mode 100644 index 000000000000..69e40d664798 --- /dev/null +++ b/changelog/66959.fixed.md @@ -0,0 +1,2 @@ +Removed the usage of wmic to get the disk and iscsi grains for Windows. The wmic +binary is being deprecated. diff --git a/changelog/66996.fixed.md b/changelog/66996.fixed.md new file mode 100644 index 000000000000..eff5079f53e7 --- /dev/null +++ b/changelog/66996.fixed.md @@ -0,0 +1 @@ +Ensured global dunders like __env__ are defined in state module that are run in parallel on spawning platforms diff --git a/changelog/66999.fixed.md b/changelog/66999.fixed.md new file mode 100644 index 000000000000..83c219f98deb --- /dev/null +++ b/changelog/66999.fixed.md @@ -0,0 +1 @@ +Filtered unpicklable objects from the context dict when invoking states in parallel on spawning platforms to avoid a crash diff --git a/requirements/base.txt b/requirements/base.txt index 290e7c44f316..fe9df1324e1e 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -10,7 +10,8 @@ requests>=2.32.3 ; python_version >= '3.10' certifi==2023.07.22; python_version < '3.10' certifi>=2024.7.4; python_version >= '3.10' distro>=1.0.1 -psutil>=5.0.0 +psutil<6.0.0; python_version <= '3.9' +psutil>=5.0.0; python_version >= '3.10' packaging>=21.3 looseversion tornado>=6.3.3 diff --git a/requirements/pytest.txt b/requirements/pytest.txt index 82dd91389585..a8a26ea38920 100644 --- a/requirements/pytest.txt +++ b/requirements/pytest.txt @@ -15,3 +15,5 @@ pyfakefs trustme pytest-skip-markers >= 1.5.2 ; python_version >= '3.8' pytest-skip-markers <= 1.5.1 ; python_version < '3.8' +pytest-shell-utilities <= 1.9.0; python_version <= '3.9' +pytest-shell-utilities >= 1.9.7; python_version >= '3.10' diff --git a/requirements/static/ci/py3.10/darwin.txt b/requirements/static/ci/py3.10/darwin.txt index e00e9bc79330..40242f1837e5 100644 --- a/requirements/static/ci/py3.10/darwin.txt +++ b/requirements/static/ci/py3.10/darwin.txt @@ -310,7 +310,7 @@ portend==3.1.0 # via # -c requirements/static/ci/../pkg/py3.10/darwin.txt # cherrypy -psutil==5.9.6 +psutil==5.9.6 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.10/darwin.txt # -r requirements/base.txt @@ -373,8 +373,10 @@ pytest-httpserver==1.0.8 # via -r requirements/pytest.txt pytest-salt-factories==1.0.1 # via -r requirements/pytest.txt -pytest-shell-utilities==1.8.0 - # via pytest-salt-factories +pytest-shell-utilities==1.9.7 ; python_version >= "3.10" + # via + # -r requirements/pytest.txt + # pytest-salt-factories pytest-skip-markers==1.5.2 ; python_version >= "3.8" # via # -r requirements/pytest.txt @@ -534,7 +536,6 @@ typing-extensions==4.8.0 # napalm # pydantic # pydantic-core - # pytest-shell-utilities # pytest-system-statistics urllib3==1.26.18 # via diff --git a/requirements/static/ci/py3.10/freebsd.txt b/requirements/static/ci/py3.10/freebsd.txt index e5a1b0d0c8d2..61df09a7d5f4 100644 --- a/requirements/static/ci/py3.10/freebsd.txt +++ b/requirements/static/ci/py3.10/freebsd.txt @@ -314,7 +314,7 @@ portend==3.1.0 # via # -c requirements/static/ci/../pkg/py3.10/freebsd.txt # cherrypy -psutil==5.9.6 +psutil==5.9.6 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.10/freebsd.txt # -r requirements/base.txt @@ -377,8 +377,10 @@ pytest-httpserver==1.0.8 # via -r requirements/pytest.txt pytest-salt-factories==1.0.1 # via -r requirements/pytest.txt -pytest-shell-utilities==1.8.0 - # via pytest-salt-factories +pytest-shell-utilities==1.9.7 ; python_version >= "3.10" + # via + # -r requirements/pytest.txt + # pytest-salt-factories pytest-skip-markers==1.5.2 ; python_version >= "3.8" # via # -r requirements/pytest.txt @@ -539,7 +541,6 @@ typing-extensions==4.8.0 # napalm # pydantic # pydantic-core - # pytest-shell-utilities # pytest-system-statistics urllib3==1.26.18 # via diff --git a/requirements/static/ci/py3.10/linux.txt b/requirements/static/ci/py3.10/linux.txt index 1ad89914cfbd..f544b863b774 100644 --- a/requirements/static/ci/py3.10/linux.txt +++ b/requirements/static/ci/py3.10/linux.txt @@ -340,7 +340,7 @@ portend==3.1.0 # via # -c requirements/static/ci/../pkg/py3.10/linux.txt # cherrypy -psutil==5.9.6 +psutil==5.9.6 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.10/linux.txt # -r requirements/base.txt @@ -411,8 +411,10 @@ pytest-httpserver==1.0.8 # via -r requirements/pytest.txt pytest-salt-factories==1.0.1 # via -r requirements/pytest.txt -pytest-shell-utilities==1.8.0 - # via pytest-salt-factories +pytest-shell-utilities==1.9.7 ; python_version >= "3.10" + # via + # -r requirements/pytest.txt + # pytest-salt-factories pytest-skip-markers==1.5.2 ; python_version >= "3.8" # via # -r requirements/pytest.txt @@ -603,7 +605,6 @@ typing-extensions==4.8.0 # napalm # pydantic # pydantic-core - # pytest-shell-utilities # pytest-system-statistics urllib3==1.26.18 # via diff --git a/requirements/static/ci/py3.10/windows.txt b/requirements/static/ci/py3.10/windows.txt index d67314a94bed..262f507d77ab 100644 --- a/requirements/static/ci/py3.10/windows.txt +++ b/requirements/static/ci/py3.10/windows.txt @@ -272,7 +272,7 @@ portend==3.1.0 # via # -c requirements/static/ci/../pkg/py3.10/windows.txt # cherrypy -psutil==5.9.6 +psutil==5.9.6 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.10/windows.txt # -r requirements/base.txt @@ -335,8 +335,10 @@ pytest-httpserver==1.0.8 # via -r requirements/pytest.txt pytest-salt-factories==1.0.1 # via -r requirements/pytest.txt -pytest-shell-utilities==1.8.0 - # via pytest-salt-factories +pytest-shell-utilities==1.9.7 ; python_version >= "3.10" + # via + # -r requirements/pytest.txt + # pytest-salt-factories pytest-skip-markers==1.5.2 ; python_version >= "3.8" # via # -r requirements/pytest.txt @@ -486,7 +488,6 @@ typing-extensions==4.8.0 # inflect # pydantic # pydantic-core - # pytest-shell-utilities # pytest-system-statistics urllib3==1.26.18 # via diff --git a/requirements/static/ci/py3.11/darwin.txt b/requirements/static/ci/py3.11/darwin.txt index 80db83a9472e..e26cde426677 100644 --- a/requirements/static/ci/py3.11/darwin.txt +++ b/requirements/static/ci/py3.11/darwin.txt @@ -303,7 +303,7 @@ portend==3.1.0 # via # -c requirements/static/ci/../pkg/py3.11/darwin.txt # cherrypy -psutil==5.9.6 +psutil==5.9.6 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.11/darwin.txt # -r requirements/base.txt @@ -366,8 +366,10 @@ pytest-httpserver==1.0.8 # via -r requirements/pytest.txt pytest-salt-factories==1.0.1 # via -r requirements/pytest.txt -pytest-shell-utilities==1.8.0 - # via pytest-salt-factories +pytest-shell-utilities==1.9.7 ; python_version >= "3.10" + # via + # -r requirements/pytest.txt + # pytest-salt-factories pytest-skip-markers==1.5.2 ; python_version >= "3.8" # via # -r requirements/pytest.txt @@ -525,7 +527,6 @@ typing-extensions==4.8.0 # napalm # pydantic # pydantic-core - # pytest-shell-utilities # pytest-system-statistics urllib3==1.26.18 # via diff --git a/requirements/static/ci/py3.11/freebsd.txt b/requirements/static/ci/py3.11/freebsd.txt index b89d11b60d20..ea00b8c225e4 100644 --- a/requirements/static/ci/py3.11/freebsd.txt +++ b/requirements/static/ci/py3.11/freebsd.txt @@ -307,7 +307,7 @@ portend==3.1.0 # via # -c requirements/static/ci/../pkg/py3.11/freebsd.txt # cherrypy -psutil==5.9.6 +psutil==5.9.6 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.11/freebsd.txt # -r requirements/base.txt @@ -370,8 +370,10 @@ pytest-httpserver==1.0.8 # via -r requirements/pytest.txt pytest-salt-factories==1.0.1 # via -r requirements/pytest.txt -pytest-shell-utilities==1.8.0 - # via pytest-salt-factories +pytest-shell-utilities==1.9.7 ; python_version >= "3.10" + # via + # -r requirements/pytest.txt + # pytest-salt-factories pytest-skip-markers==1.5.2 ; python_version >= "3.8" # via # -r requirements/pytest.txt @@ -531,7 +533,6 @@ typing-extensions==4.8.0 # napalm # pydantic # pydantic-core - # pytest-shell-utilities # pytest-system-statistics urllib3==1.26.18 # via diff --git a/requirements/static/ci/py3.11/linux.txt b/requirements/static/ci/py3.11/linux.txt index 320b49135b8b..4fc21c945fbc 100644 --- a/requirements/static/ci/py3.11/linux.txt +++ b/requirements/static/ci/py3.11/linux.txt @@ -331,7 +331,7 @@ portend==3.1.0 # via # -c requirements/static/ci/../pkg/py3.11/linux.txt # cherrypy -psutil==5.9.6 +psutil==5.9.6 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.11/linux.txt # -r requirements/base.txt @@ -402,8 +402,10 @@ pytest-httpserver==1.0.8 # via -r requirements/pytest.txt pytest-salt-factories==1.0.1 # via -r requirements/pytest.txt -pytest-shell-utilities==1.8.0 - # via pytest-salt-factories +pytest-shell-utilities==1.9.7 ; python_version >= "3.10" + # via + # -r requirements/pytest.txt + # pytest-salt-factories pytest-skip-markers==1.5.2 ; python_version >= "3.8" # via # -r requirements/pytest.txt @@ -593,7 +595,6 @@ typing-extensions==4.8.0 # napalm # pydantic # pydantic-core - # pytest-shell-utilities # pytest-system-statistics urllib3==1.26.18 # via diff --git a/requirements/static/ci/py3.11/windows.txt b/requirements/static/ci/py3.11/windows.txt index 5116f0802523..ff15720264ad 100644 --- a/requirements/static/ci/py3.11/windows.txt +++ b/requirements/static/ci/py3.11/windows.txt @@ -265,7 +265,7 @@ portend==3.1.0 # via # -c requirements/static/ci/../pkg/py3.11/windows.txt # cherrypy -psutil==5.9.6 +psutil==5.9.6 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.11/windows.txt # -r requirements/base.txt @@ -328,8 +328,10 @@ pytest-httpserver==1.0.8 # via -r requirements/pytest.txt pytest-salt-factories==1.0.1 # via -r requirements/pytest.txt -pytest-shell-utilities==1.8.0 - # via pytest-salt-factories +pytest-shell-utilities==1.9.7 ; python_version >= "3.10" + # via + # -r requirements/pytest.txt + # pytest-salt-factories pytest-skip-markers==1.5.2 ; python_version >= "3.8" # via # -r requirements/pytest.txt @@ -477,7 +479,6 @@ typing-extensions==4.8.0 # inflect # pydantic # pydantic-core - # pytest-shell-utilities # pytest-system-statistics urllib3==1.26.18 # via diff --git a/requirements/static/ci/py3.12/cloud.txt b/requirements/static/ci/py3.12/cloud.txt index 4fae751b1d0d..fd7728e7fccb 100644 --- a/requirements/static/ci/py3.12/cloud.txt +++ b/requirements/static/ci/py3.12/cloud.txt @@ -427,7 +427,7 @@ portend==3.1.0 # cherrypy profitbricks==4.1.3 # via -r requirements/static/ci/cloud.in -psutil==5.9.6 +psutil==5.9.6 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.12/linux.txt # -c requirements/static/ci/py3.12/linux.txt @@ -525,9 +525,10 @@ pytest-salt-factories==1.0.1 # via # -c requirements/static/ci/py3.12/linux.txt # -r requirements/pytest.txt -pytest-shell-utilities==1.8.0 +pytest-shell-utilities==1.9.7 ; python_version >= "3.10" # via # -c requirements/static/ci/py3.12/linux.txt + # -r requirements/pytest.txt # pytest-salt-factories pytest-skip-markers==1.5.2 ; python_version >= "3.8" # via @@ -758,7 +759,6 @@ typing-extensions==4.8.0 # napalm # pydantic # pydantic-core - # pytest-shell-utilities # pytest-system-statistics urllib3==1.26.18 # via diff --git a/requirements/static/ci/py3.12/darwin.txt b/requirements/static/ci/py3.12/darwin.txt index 4f5b37dfae27..8d8ec9c504f1 100644 --- a/requirements/static/ci/py3.12/darwin.txt +++ b/requirements/static/ci/py3.12/darwin.txt @@ -303,7 +303,7 @@ portend==3.1.0 # via # -c requirements/static/ci/../pkg/py3.12/darwin.txt # cherrypy -psutil==5.9.6 +psutil==5.9.6 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.12/darwin.txt # -r requirements/base.txt @@ -366,8 +366,10 @@ pytest-httpserver==1.0.8 # via -r requirements/pytest.txt pytest-salt-factories==1.0.1 # via -r requirements/pytest.txt -pytest-shell-utilities==1.8.0 - # via pytest-salt-factories +pytest-shell-utilities==1.9.7 ; python_version >= "3.10" + # via + # -r requirements/pytest.txt + # pytest-salt-factories pytest-skip-markers==1.5.2 ; python_version >= "3.8" # via # -r requirements/pytest.txt @@ -525,7 +527,6 @@ typing-extensions==4.8.0 # napalm # pydantic # pydantic-core - # pytest-shell-utilities # pytest-system-statistics urllib3==1.26.18 # via diff --git a/requirements/static/ci/py3.12/docs.txt b/requirements/static/ci/py3.12/docs.txt index 259db0bdd155..f1b146882eda 100644 --- a/requirements/static/ci/py3.12/docs.txt +++ b/requirements/static/ci/py3.12/docs.txt @@ -168,7 +168,7 @@ portend==3.1.0 # via # -c requirements/static/ci/py3.12/linux.txt # cherrypy -psutil==5.9.6 +psutil==5.9.6 ; python_version >= "3.10" # via # -c requirements/static/ci/py3.12/linux.txt # -r requirements/base.txt diff --git a/requirements/static/ci/py3.12/freebsd.txt b/requirements/static/ci/py3.12/freebsd.txt index f0ddabf0883c..f151e877d33f 100644 --- a/requirements/static/ci/py3.12/freebsd.txt +++ b/requirements/static/ci/py3.12/freebsd.txt @@ -307,7 +307,7 @@ portend==3.1.0 # via # -c requirements/static/ci/../pkg/py3.12/freebsd.txt # cherrypy -psutil==5.9.6 +psutil==5.9.6 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.12/freebsd.txt # -r requirements/base.txt @@ -370,8 +370,10 @@ pytest-httpserver==1.0.8 # via -r requirements/pytest.txt pytest-salt-factories==1.0.1 # via -r requirements/pytest.txt -pytest-shell-utilities==1.8.0 - # via pytest-salt-factories +pytest-shell-utilities==1.9.7 ; python_version >= "3.10" + # via + # -r requirements/pytest.txt + # pytest-salt-factories pytest-skip-markers==1.5.2 ; python_version >= "3.8" # via # -r requirements/pytest.txt @@ -531,7 +533,6 @@ typing-extensions==4.8.0 # napalm # pydantic # pydantic-core - # pytest-shell-utilities # pytest-system-statistics urllib3==1.26.18 # via diff --git a/requirements/static/ci/py3.12/lint.txt b/requirements/static/ci/py3.12/lint.txt index 5e9d76966126..715314c36297 100644 --- a/requirements/static/ci/py3.12/lint.txt +++ b/requirements/static/ci/py3.12/lint.txt @@ -453,7 +453,7 @@ portend==3.1.0 # -c requirements/static/ci/../pkg/py3.12/linux.txt # -c requirements/static/ci/py3.12/linux.txt # cherrypy -psutil==5.9.6 +psutil==5.9.6 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.12/linux.txt # -c requirements/static/ci/py3.12/linux.txt diff --git a/requirements/static/ci/py3.12/linux.txt b/requirements/static/ci/py3.12/linux.txt index 1b5c42d70d71..9eeaef8b3972 100644 --- a/requirements/static/ci/py3.12/linux.txt +++ b/requirements/static/ci/py3.12/linux.txt @@ -331,7 +331,7 @@ portend==3.1.0 # via # -c requirements/static/ci/../pkg/py3.12/linux.txt # cherrypy -psutil==5.9.6 +psutil==5.9.6 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.12/linux.txt # -r requirements/base.txt @@ -402,8 +402,10 @@ pytest-httpserver==1.0.8 # via -r requirements/pytest.txt pytest-salt-factories==1.0.1 # via -r requirements/pytest.txt -pytest-shell-utilities==1.8.0 - # via pytest-salt-factories +pytest-shell-utilities==1.9.7 ; python_version >= "3.10" + # via + # -r requirements/pytest.txt + # pytest-salt-factories pytest-skip-markers==1.5.2 ; python_version >= "3.8" # via # -r requirements/pytest.txt @@ -593,7 +595,6 @@ typing-extensions==4.8.0 # napalm # pydantic # pydantic-core - # pytest-shell-utilities # pytest-system-statistics urllib3==1.26.18 # via diff --git a/requirements/static/ci/py3.12/windows.txt b/requirements/static/ci/py3.12/windows.txt index 0f40fd95a55e..f1deb4869ced 100644 --- a/requirements/static/ci/py3.12/windows.txt +++ b/requirements/static/ci/py3.12/windows.txt @@ -265,7 +265,7 @@ portend==3.1.0 # via # -c requirements/static/ci/../pkg/py3.12/windows.txt # cherrypy -psutil==5.9.6 +psutil==5.9.6 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.12/windows.txt # -r requirements/base.txt @@ -328,8 +328,10 @@ pytest-httpserver==1.0.8 # via -r requirements/pytest.txt pytest-salt-factories==1.0.1 # via -r requirements/pytest.txt -pytest-shell-utilities==1.8.0 - # via pytest-salt-factories +pytest-shell-utilities==1.9.7 ; python_version >= "3.10" + # via + # -r requirements/pytest.txt + # pytest-salt-factories pytest-skip-markers==1.5.2 ; python_version >= "3.8" # via # -r requirements/pytest.txt @@ -477,7 +479,6 @@ typing-extensions==4.8.0 # inflect # pydantic # pydantic-core - # pytest-shell-utilities # pytest-system-statistics urllib3==1.26.18 # via diff --git a/requirements/static/ci/py3.8/freebsd.txt b/requirements/static/ci/py3.8/freebsd.txt index 2d8eb95d7f8f..c1fc7c559845 100644 --- a/requirements/static/ci/py3.8/freebsd.txt +++ b/requirements/static/ci/py3.8/freebsd.txt @@ -318,7 +318,7 @@ portend==3.1.0 # via # -c requirements/static/ci/../pkg/py3.8/freebsd.txt # cherrypy -psutil==5.9.6 +psutil==5.9.6 ; python_version <= "3.9" # via # -c requirements/static/ci/../pkg/py3.8/freebsd.txt # -r requirements/base.txt @@ -381,8 +381,10 @@ pytest-httpserver==1.0.8 # via -r requirements/pytest.txt pytest-salt-factories==1.0.1 # via -r requirements/pytest.txt -pytest-shell-utilities==1.8.0 - # via pytest-salt-factories +pytest-shell-utilities==1.8.0 ; python_version <= "3.9" + # via + # -r requirements/pytest.txt + # pytest-salt-factories pytest-skip-markers==1.5.2 ; python_version >= "3.8" # via # -r requirements/pytest.txt diff --git a/requirements/static/ci/py3.8/linux.txt b/requirements/static/ci/py3.8/linux.txt index c0000d4aefb8..518e9ae900d3 100644 --- a/requirements/static/ci/py3.8/linux.txt +++ b/requirements/static/ci/py3.8/linux.txt @@ -337,7 +337,7 @@ portend==3.1.0 # via # -c requirements/static/ci/../pkg/py3.8/linux.txt # cherrypy -psutil==5.9.6 +psutil==5.9.6 ; python_version <= "3.9" # via # -c requirements/static/ci/../pkg/py3.8/linux.txt # -r requirements/base.txt @@ -408,8 +408,10 @@ pytest-httpserver==1.0.8 # via -r requirements/pytest.txt pytest-salt-factories==1.0.1 # via -r requirements/pytest.txt -pytest-shell-utilities==1.8.0 - # via pytest-salt-factories +pytest-shell-utilities==1.8.0 ; python_version <= "3.9" + # via + # -r requirements/pytest.txt + # pytest-salt-factories pytest-skip-markers==1.5.2 ; python_version >= "3.8" # via # -r requirements/pytest.txt diff --git a/requirements/static/ci/py3.8/windows.txt b/requirements/static/ci/py3.8/windows.txt index cc780bc39f8e..a863cd7cc351 100644 --- a/requirements/static/ci/py3.8/windows.txt +++ b/requirements/static/ci/py3.8/windows.txt @@ -276,7 +276,7 @@ portend==3.1.0 # via # -c requirements/static/ci/../pkg/py3.8/windows.txt # cherrypy -psutil==5.9.6 +psutil==5.9.6 ; python_version <= "3.9" # via # -c requirements/static/ci/../pkg/py3.8/windows.txt # -r requirements/base.txt @@ -339,8 +339,10 @@ pytest-httpserver==1.0.8 # via -r requirements/pytest.txt pytest-salt-factories==1.0.1 # via -r requirements/pytest.txt -pytest-shell-utilities==1.8.0 - # via pytest-salt-factories +pytest-shell-utilities==1.8.0 ; python_version <= "3.9" + # via + # -r requirements/pytest.txt + # pytest-salt-factories pytest-skip-markers==1.5.2 ; python_version >= "3.8" # via # -r requirements/pytest.txt diff --git a/requirements/static/ci/py3.9/darwin.txt b/requirements/static/ci/py3.9/darwin.txt index 5af323cd2be1..28c6cfb4278d 100644 --- a/requirements/static/ci/py3.9/darwin.txt +++ b/requirements/static/ci/py3.9/darwin.txt @@ -310,7 +310,7 @@ portend==3.1.0 # via # -c requirements/static/ci/../pkg/py3.9/darwin.txt # cherrypy -psutil==5.9.6 +psutil==5.9.6 ; python_version <= "3.9" # via # -c requirements/static/ci/../pkg/py3.9/darwin.txt # -r requirements/base.txt @@ -373,8 +373,10 @@ pytest-httpserver==1.0.8 # via -r requirements/pytest.txt pytest-salt-factories==1.0.1 # via -r requirements/pytest.txt -pytest-shell-utilities==1.8.0 - # via pytest-salt-factories +pytest-shell-utilities==1.8.0 ; python_version <= "3.9" + # via + # -r requirements/pytest.txt + # pytest-salt-factories pytest-skip-markers==1.5.2 ; python_version >= "3.8" # via # -r requirements/pytest.txt diff --git a/requirements/static/ci/py3.9/freebsd.txt b/requirements/static/ci/py3.9/freebsd.txt index 6600b0c848f5..d9debff52c96 100644 --- a/requirements/static/ci/py3.9/freebsd.txt +++ b/requirements/static/ci/py3.9/freebsd.txt @@ -314,7 +314,7 @@ portend==3.1.0 # via # -c requirements/static/ci/../pkg/py3.9/freebsd.txt # cherrypy -psutil==5.9.6 +psutil==5.9.6 ; python_version <= "3.9" # via # -c requirements/static/ci/../pkg/py3.9/freebsd.txt # -r requirements/base.txt @@ -377,8 +377,10 @@ pytest-httpserver==1.0.8 # via -r requirements/pytest.txt pytest-salt-factories==1.0.1 # via -r requirements/pytest.txt -pytest-shell-utilities==1.8.0 - # via pytest-salt-factories +pytest-shell-utilities==1.8.0 ; python_version <= "3.9" + # via + # -r requirements/pytest.txt + # pytest-salt-factories pytest-skip-markers==1.5.2 ; python_version >= "3.8" # via # -r requirements/pytest.txt diff --git a/requirements/static/ci/py3.9/linux.txt b/requirements/static/ci/py3.9/linux.txt index 044d802af8aa..249ea7f574a2 100644 --- a/requirements/static/ci/py3.9/linux.txt +++ b/requirements/static/ci/py3.9/linux.txt @@ -333,7 +333,7 @@ portend==3.1.0 # via # -c requirements/static/ci/../pkg/py3.9/linux.txt # cherrypy -psutil==5.9.6 +psutil==5.9.6 ; python_version <= "3.9" # via # -c requirements/static/ci/../pkg/py3.9/linux.txt # -r requirements/base.txt @@ -404,8 +404,10 @@ pytest-httpserver==1.0.8 # via -r requirements/pytest.txt pytest-salt-factories==1.0.1 # via -r requirements/pytest.txt -pytest-shell-utilities==1.8.0 - # via pytest-salt-factories +pytest-shell-utilities==1.8.0 ; python_version <= "3.9" + # via + # -r requirements/pytest.txt + # pytest-salt-factories pytest-skip-markers==1.5.2 ; python_version >= "3.8" # via # -r requirements/pytest.txt diff --git a/requirements/static/ci/py3.9/windows.txt b/requirements/static/ci/py3.9/windows.txt index 82ece83ae3af..ff210ca14b20 100644 --- a/requirements/static/ci/py3.9/windows.txt +++ b/requirements/static/ci/py3.9/windows.txt @@ -272,7 +272,7 @@ portend==3.1.0 # via # -c requirements/static/ci/../pkg/py3.9/windows.txt # cherrypy -psutil==5.9.6 +psutil==5.9.6 ; python_version <= "3.9" # via # -c requirements/static/ci/../pkg/py3.9/windows.txt # -r requirements/base.txt @@ -335,8 +335,10 @@ pytest-httpserver==1.0.8 # via -r requirements/pytest.txt pytest-salt-factories==1.0.1 # via -r requirements/pytest.txt -pytest-shell-utilities==1.8.0 - # via pytest-salt-factories +pytest-shell-utilities==1.8.0 ; python_version <= "3.9" + # via + # -r requirements/pytest.txt + # pytest-salt-factories pytest-skip-markers==1.5.2 ; python_version >= "3.8" # via # -r requirements/pytest.txt diff --git a/requirements/static/pkg/py3.10/darwin.txt b/requirements/static/pkg/py3.10/darwin.txt index 67bed9a326b3..595d3a7cbd9f 100644 --- a/requirements/static/pkg/py3.10/darwin.txt +++ b/requirements/static/pkg/py3.10/darwin.txt @@ -89,7 +89,7 @@ packaging==23.1 # via -r requirements/base.txt portend==3.1.0 # via cherrypy -psutil==5.9.6 +psutil==5.9.6 ; python_version >= "3.10" # via -r requirements/base.txt pycparser==2.21 # via cffi diff --git a/requirements/static/pkg/py3.10/freebsd.txt b/requirements/static/pkg/py3.10/freebsd.txt index a6286ee42f36..7b924ca00a54 100644 --- a/requirements/static/pkg/py3.10/freebsd.txt +++ b/requirements/static/pkg/py3.10/freebsd.txt @@ -89,7 +89,7 @@ packaging==23.1 # via -r requirements/base.txt portend==3.1.0 # via cherrypy -psutil==5.9.6 +psutil==5.9.6 ; python_version >= "3.10" # via -r requirements/base.txt pycparser==2.21 # via cffi diff --git a/requirements/static/pkg/py3.10/linux.txt b/requirements/static/pkg/py3.10/linux.txt index 832feb520850..49cd88ed3c3b 100644 --- a/requirements/static/pkg/py3.10/linux.txt +++ b/requirements/static/pkg/py3.10/linux.txt @@ -89,7 +89,7 @@ packaging==23.1 # via -r requirements/base.txt portend==3.1.0 # via cherrypy -psutil==5.9.6 +psutil==5.9.6 ; python_version >= "3.10" # via -r requirements/base.txt pycparser==2.21 # via cffi diff --git a/requirements/static/pkg/py3.10/windows.txt b/requirements/static/pkg/py3.10/windows.txt index 7b131da556f7..37cdd9826a52 100644 --- a/requirements/static/pkg/py3.10/windows.txt +++ b/requirements/static/pkg/py3.10/windows.txt @@ -97,7 +97,7 @@ packaging==23.1 # via -r requirements/base.txt portend==3.1.0 # via cherrypy -psutil==5.9.6 +psutil==5.9.6 ; python_version >= "3.10" # via -r requirements/base.txt pycparser==2.21 # via cffi diff --git a/requirements/static/pkg/py3.11/darwin.txt b/requirements/static/pkg/py3.11/darwin.txt index 7d16335c1799..aa873046d750 100644 --- a/requirements/static/pkg/py3.11/darwin.txt +++ b/requirements/static/pkg/py3.11/darwin.txt @@ -87,7 +87,7 @@ packaging==23.1 # via -r requirements/base.txt portend==3.1.0 # via cherrypy -psutil==5.9.6 +psutil==5.9.6 ; python_version >= "3.10" # via -r requirements/base.txt pycparser==2.21 # via cffi diff --git a/requirements/static/pkg/py3.11/freebsd.txt b/requirements/static/pkg/py3.11/freebsd.txt index b42b9373574f..98912ee2a555 100644 --- a/requirements/static/pkg/py3.11/freebsd.txt +++ b/requirements/static/pkg/py3.11/freebsd.txt @@ -87,7 +87,7 @@ packaging==23.1 # via -r requirements/base.txt portend==3.1.0 # via cherrypy -psutil==5.9.6 +psutil==5.9.6 ; python_version >= "3.10" # via -r requirements/base.txt pycparser==2.21 # via cffi diff --git a/requirements/static/pkg/py3.11/linux.txt b/requirements/static/pkg/py3.11/linux.txt index e5c4d0959579..a54387e807c6 100644 --- a/requirements/static/pkg/py3.11/linux.txt +++ b/requirements/static/pkg/py3.11/linux.txt @@ -87,7 +87,7 @@ packaging==23.1 # via -r requirements/base.txt portend==3.1.0 # via cherrypy -psutil==5.9.6 +psutil==5.9.6 ; python_version >= "3.10" # via -r requirements/base.txt pycparser==2.21 # via cffi diff --git a/requirements/static/pkg/py3.11/windows.txt b/requirements/static/pkg/py3.11/windows.txt index 8c3468258e4c..e9c7b06e3207 100644 --- a/requirements/static/pkg/py3.11/windows.txt +++ b/requirements/static/pkg/py3.11/windows.txt @@ -95,7 +95,7 @@ packaging==23.1 # via -r requirements/base.txt portend==3.1.0 # via cherrypy -psutil==5.9.6 +psutil==5.9.6 ; python_version >= "3.10" # via -r requirements/base.txt pycparser==2.21 # via cffi diff --git a/requirements/static/pkg/py3.12/darwin.txt b/requirements/static/pkg/py3.12/darwin.txt index 2d91e153bbf8..ebce1ec3b829 100644 --- a/requirements/static/pkg/py3.12/darwin.txt +++ b/requirements/static/pkg/py3.12/darwin.txt @@ -87,7 +87,7 @@ packaging==23.1 # via -r requirements/base.txt portend==3.1.0 # via cherrypy -psutil==5.9.6 +psutil==5.9.6 ; python_version >= "3.10" # via -r requirements/base.txt pycparser==2.21 # via cffi diff --git a/requirements/static/pkg/py3.12/freebsd.txt b/requirements/static/pkg/py3.12/freebsd.txt index b8fc5f597429..c741fd717842 100644 --- a/requirements/static/pkg/py3.12/freebsd.txt +++ b/requirements/static/pkg/py3.12/freebsd.txt @@ -87,7 +87,7 @@ packaging==23.1 # via -r requirements/base.txt portend==3.1.0 # via cherrypy -psutil==5.9.6 +psutil==5.9.6 ; python_version >= "3.10" # via -r requirements/base.txt pycparser==2.21 # via cffi diff --git a/requirements/static/pkg/py3.12/linux.txt b/requirements/static/pkg/py3.12/linux.txt index f048c510ee85..a66ffb814bea 100644 --- a/requirements/static/pkg/py3.12/linux.txt +++ b/requirements/static/pkg/py3.12/linux.txt @@ -87,7 +87,7 @@ packaging==23.1 # via -r requirements/base.txt portend==3.1.0 # via cherrypy -psutil==5.9.6 +psutil==5.9.6 ; python_version >= "3.10" # via -r requirements/base.txt pycparser==2.21 # via cffi diff --git a/requirements/static/pkg/py3.12/windows.txt b/requirements/static/pkg/py3.12/windows.txt index 01f9e4c4669b..79c33f9258f8 100644 --- a/requirements/static/pkg/py3.12/windows.txt +++ b/requirements/static/pkg/py3.12/windows.txt @@ -95,7 +95,7 @@ packaging==23.1 # via -r requirements/base.txt portend==3.1.0 # via cherrypy -psutil==5.9.6 +psutil==5.9.6 ; python_version >= "3.10" # via -r requirements/base.txt pycparser==2.21 # via cffi diff --git a/requirements/static/pkg/py3.8/freebsd.txt b/requirements/static/pkg/py3.8/freebsd.txt index 2becd72d10f1..ffde6ff5abbe 100644 --- a/requirements/static/pkg/py3.8/freebsd.txt +++ b/requirements/static/pkg/py3.8/freebsd.txt @@ -91,7 +91,7 @@ packaging==23.1 # via -r requirements/base.txt portend==3.1.0 # via cherrypy -psutil==5.9.6 +psutil==5.9.6 ; python_version <= "3.9" # via -r requirements/base.txt pycparser==2.21 # via cffi diff --git a/requirements/static/pkg/py3.8/linux.txt b/requirements/static/pkg/py3.8/linux.txt index 843b5c5e1892..4a904ec9c537 100644 --- a/requirements/static/pkg/py3.8/linux.txt +++ b/requirements/static/pkg/py3.8/linux.txt @@ -91,7 +91,7 @@ packaging==23.1 # via -r requirements/base.txt portend==3.1.0 # via cherrypy -psutil==5.9.6 +psutil==5.9.6 ; python_version <= "3.9" # via -r requirements/base.txt pycparser==2.21 # via cffi diff --git a/requirements/static/pkg/py3.8/windows.txt b/requirements/static/pkg/py3.8/windows.txt index fd7e78c8c22f..b5a05ca4464d 100644 --- a/requirements/static/pkg/py3.8/windows.txt +++ b/requirements/static/pkg/py3.8/windows.txt @@ -99,7 +99,7 @@ packaging==23.1 # via -r requirements/base.txt portend==3.1.0 # via cherrypy -psutil==5.9.6 +psutil==5.9.6 ; python_version <= "3.9" # via -r requirements/base.txt pycparser==2.21 # via cffi diff --git a/requirements/static/pkg/py3.9/darwin.txt b/requirements/static/pkg/py3.9/darwin.txt index 7635b7f0fa4e..5f547422f75f 100644 --- a/requirements/static/pkg/py3.9/darwin.txt +++ b/requirements/static/pkg/py3.9/darwin.txt @@ -89,7 +89,7 @@ packaging==23.1 # via -r requirements/base.txt portend==3.1.0 # via cherrypy -psutil==5.9.6 +psutil==5.9.6 ; python_version <= "3.9" # via -r requirements/base.txt pycparser==2.21 # via cffi diff --git a/requirements/static/pkg/py3.9/freebsd.txt b/requirements/static/pkg/py3.9/freebsd.txt index 698679ed73ab..c3dd38187b7a 100644 --- a/requirements/static/pkg/py3.9/freebsd.txt +++ b/requirements/static/pkg/py3.9/freebsd.txt @@ -89,7 +89,7 @@ packaging==23.1 # via -r requirements/base.txt portend==3.1.0 # via cherrypy -psutil==5.9.6 +psutil==5.9.6 ; python_version <= "3.9" # via -r requirements/base.txt pycparser==2.21 # via cffi diff --git a/requirements/static/pkg/py3.9/linux.txt b/requirements/static/pkg/py3.9/linux.txt index 99b1e302ec2e..d73ec84b915d 100644 --- a/requirements/static/pkg/py3.9/linux.txt +++ b/requirements/static/pkg/py3.9/linux.txt @@ -89,7 +89,7 @@ packaging==23.1 # via -r requirements/base.txt portend==3.1.0 # via cherrypy -psutil==5.9.6 +psutil==5.9.6 ; python_version <= "3.9" # via -r requirements/base.txt pycparser==2.21 # via cffi diff --git a/requirements/static/pkg/py3.9/windows.txt b/requirements/static/pkg/py3.9/windows.txt index 1ae148189523..c3aad36e7278 100644 --- a/requirements/static/pkg/py3.9/windows.txt +++ b/requirements/static/pkg/py3.9/windows.txt @@ -97,7 +97,7 @@ packaging==23.1 # via -r requirements/base.txt portend==3.1.0 # via cherrypy -psutil==5.9.6 +psutil==5.9.6 ; python_version <= "3.9" # via -r requirements/base.txt pycparser==2.21 # via cffi diff --git a/salt/grains/disks.py b/salt/grains/disks.py index f61ad6d6b34b..7a3f04827706 100644 --- a/salt/grains/disks.py +++ b/salt/grains/disks.py @@ -16,6 +16,7 @@ __salt__ = { "cmd.run": salt.modules.cmdmod._run_quiet, "cmd.run_all": salt.modules.cmdmod._run_all_quiet, + "cmd.powershell": salt.modules.cmdmod.powershell, } log = logging.getLogger(__name__) @@ -153,41 +154,28 @@ def _linux_disks(): def _windows_disks(): - wmic = salt.utils.path.which("wmic") - - namespace = r"\\root\microsoft\windows\storage" - path = "MSFT_PhysicalDisk" - get = "DeviceID,MediaType" + cmd = "Get-PhysicalDisk | Select DeviceID, MediaType" ret = {"disks": [], "ssds": []} - cmdret = __salt__["cmd.run_all"]( - "{} /namespace:{} path {} get {} /format:table".format( - wmic, namespace, path, get - ) - ) - - if cmdret["retcode"] != 0: - log.trace("Disk grain does not support this version of Windows") - else: - for line in cmdret["stdout"].splitlines(): - info = line.split() - if len(info) != 2 or not info[0].isdigit() or not info[1].isdigit(): - continue - device = rf"\\.\PhysicalDrive{info[0]}" - mediatype = info[1] - if mediatype == "3": - log.trace("Device %s reports itself as an HDD", device) - ret["disks"].append(device) - elif mediatype == "4": - log.trace("Device %s reports itself as an SSD", device) - ret["ssds"].append(device) - ret["disks"].append(device) - elif mediatype == "5": - log.trace("Device %s reports itself as an SCM", device) - ret["disks"].append(device) - else: - log.trace("Device %s reports itself as Unspecified", device) - ret["disks"].append(device) + drive_info = __salt__["cmd.powershell"](cmd) + + if not drive_info: + log.trace("No physical discs found") + return ret + + # We need a list of dict + if isinstance(drive_info, dict): + drive_info = [drive_info] + + for drive in drive_info: + # Make sure we have a valid drive type + if drive["MediaType"].lower() not in ["hdd", "ssd", "scm", "unspecified"]: + log.trace(f'Unknown media type: {drive["MediaType"]}') + continue + device = rf'\\.\PhysicalDrive{drive["DeviceID"]}' + ret["disks"].append(device) + if drive["MediaType"].lower() == "ssd": + ret["ssds"].append(device) return ret diff --git a/salt/grains/iscsi.py b/salt/grains/iscsi.py index 64199d6e99d7..62c4eccc7199 100644 --- a/salt/grains/iscsi.py +++ b/salt/grains/iscsi.py @@ -85,28 +85,25 @@ def _aix_iqn(): def _windows_iqn(): """ - Return iSCSI IQN from a Windows host. + Return iSCSI nodes from a Windows host. """ + cmd = "Get-InitiatorPort | Select NodeAddress" ret = [] - wmic = salt.utils.path.which("wmic") + nodes = salt.modules.cmdmod.powershell(cmd) - if not wmic: + if not nodes: + log.trace("No iSCSI nodes found") return ret - namespace = r"\\root\WMI" - path = "MSiSCSIInitiator_MethodClass" - get = "iSCSINodeName" + # A single node will return a dictionary with a single entry + # {"NodeAddress": "iqn.1991-05.com.microsoft:johnj99-pc2.contoso.com"} + # Multiple nodes will return a list of single entry dicts + # We need a list of dict + if isinstance(nodes, dict): + nodes = [nodes] - cmd_ret = salt.modules.cmdmod.run_all( - "{} /namespace:{} path {} get {} /format:table".format( - wmic, namespace, path, get - ) - ) - - for line in cmd_ret["stdout"].splitlines(): - if line.startswith("iqn."): - line = line.rstrip() - ret.append(line.rstrip()) + for node in nodes: + ret.append(node["NodeAddress"]) return ret diff --git a/salt/modules/cmdmod.py b/salt/modules/cmdmod.py index 1891c044fb03..e708c10c0b00 100644 --- a/salt/modules/cmdmod.py +++ b/salt/modules/cmdmod.py @@ -266,7 +266,7 @@ def _prep_powershell_cmd(win_shell, cmd, encoded_cmd): win_shell = salt.utils.path.which(win_shell) if not win_shell: - raise CommandExecutionError("PowerShell binary not found") + raise CommandExecutionError(f"PowerShell binary not found: {win_shell}") new_cmd = [win_shell, "-NonInteractive", "-NoProfile", "-ExecutionPolicy", "Bypass"] diff --git a/salt/modules/selinux.py b/salt/modules/selinux.py index 21471397ea4e..0f78d1be9c6f 100644 --- a/salt/modules/selinux.py +++ b/salt/modules/selinux.py @@ -490,7 +490,7 @@ def fcontext_get_policy( "[[:alpha:] ]+" if filetype is None else filetype_id_to_string(filetype) ) cmd = ( - "semanage fcontext -l | egrep " + "semanage fcontext -l | grep -E " + "'^{filespec}{spacer}{filetype}{spacer}{sel_user}:{sel_role}:{sel_type}:{sel_level}{ospacer}$'".format( **cmd_kwargs ) @@ -616,7 +616,7 @@ def _fcontext_add_or_delete_policy( if "add" == action: # need to use --modify if context for name file exists, otherwise ValueError filespec = re.escape(name) - cmd = f"semanage fcontext -l | egrep '{filespec}'" + cmd = f"semanage fcontext -l | grep -E '{filespec} '" current_entry_text = __salt__["cmd.shell"](cmd, ignore_retcode=True) if current_entry_text != "": action = "modify" @@ -762,7 +762,7 @@ def port_get_policy(name, sel_type=None, protocol=None, port=None): "port": port, } cmd = ( - "semanage port -l | egrep " + "semanage port -l | grep -E " + "'^{sel_type}{spacer}{protocol}{spacer}((.*)*)[ ]{port}($|,)'".format( **cmd_kwargs ) diff --git a/salt/modules/yumpkg.py b/salt/modules/yumpkg.py index fccf095fcc9a..7c5c017fc89b 100644 --- a/salt/modules/yumpkg.py +++ b/salt/modules/yumpkg.py @@ -318,7 +318,8 @@ def _get_options(**kwargs): ret.append(f"--branch={branch}") for item in setopt: - ret.extend(["--setopt", str(item)]) + log.info("Adding configuration option '%s'", item) + ret.extend([f"--setopt={item}"]) if get_extra_options: # sorting here to make order uniform, makes unit testing more reliable diff --git a/salt/state.py b/salt/state.py index 531c7801bf01..15a17742c3b3 100644 --- a/salt/state.py +++ b/salt/state.py @@ -18,6 +18,7 @@ import inspect import logging import os +import pickle import random import re import site @@ -757,21 +758,21 @@ def __init__( loader="states", initial_pillar=None, file_client=None, - __invocation_id=None, + _invocation_id=None, ): """ When instantiating an object of this class, do not pass - ``__invocation_id``. It is an internal field for tracking + ``_invocation_id``. It is an internal field for tracking parallel executions where no jid is available (Salt-SSH) and only exposed as an init argument to work on spawning platforms. """ if jid is not None: - __invocation_id = jid - if __invocation_id is None: + _invocation_id = jid + if _invocation_id is None: # For salt-ssh parallel states, we need a unique identifier # for a single execution. self.jid should not be set there # since it's used for other purposes as well. - __invocation_id = salt.utils.jid.gen_jid(opts) + _invocation_id = salt.utils.jid.gen_jid(opts) self._init_kwargs = { "opts": opts, "pillar_override": pillar_override, @@ -782,7 +783,7 @@ def __init__( "mocked": mocked, "loader": loader, "initial_pillar": initial_pillar, - "__invocation_id": __invocation_id, + "_invocation_id": _invocation_id, } self.states_loader = loader if "grains" not in opts: @@ -829,7 +830,7 @@ def __init__( self.pre = {} self.__run_num = 0 self.jid = jid - self.invocation_id = __invocation_id + self.invocation_id = _invocation_id self.instance_id = str(id(self)) self.inject_globals = {} self.mocked = mocked @@ -2157,12 +2158,15 @@ def requisite_in(self, high): return req_in_high, errors @classmethod - def _call_parallel_target(cls, instance, init_kwargs, name, cdata, low): + def _call_parallel_target( + cls, instance, init_kwargs, name, cdata, low, inject_globals + ): """ The target function to call that will create the parallel thread/process """ if instance is None: instance = cls(**init_kwargs) + instance.states.inject_globals = inject_globals # we need to re-record start/end duration here because it is impossible to # correctly calculate further down the chain utc_start_time = datetime.datetime.utcnow() @@ -2267,7 +2271,7 @@ def _call_parallel_target(cls, instance, init_kwargs, name, cdata, low): with salt.utils.files.fopen(tfile, "wb+") as fp_: fp_.write(msgpack_serialize(ret)) - def call_parallel(self, cdata, low): + def call_parallel(self, cdata, low, inject_globals): """ Call the state defined in the given cdata in parallel """ @@ -2284,13 +2288,37 @@ def call_parallel(self, cdata, low): instance = None else: instance = self + inject_globals = None proc = salt.utils.process.Process( target=self._call_parallel_target, - args=(instance, self._init_kwargs, name, cdata, low), + args=(instance, self._init_kwargs, name, cdata, low, inject_globals), name=f"ParallelState({name})", ) - proc.start() + try: + proc.start() + except TypeError as err: + # Some modules use the context to cache unpicklable objects like + # database connections or loader instances. + # Ensure we don't crash because of that on spawning platforms. + if "cannot pickle" not in str(err): + raise + clean_context = {} + for var, val in self._init_kwargs["context"].items(): + try: + pickle.dumps(val) + except TypeError: + pass + else: + clean_context[var] = val + init_kwargs = self._init_kwargs.copy() + init_kwargs["context"] = clean_context + proc = salt.utils.process.Process( + target=self._call_parallel_target, + args=(instance, init_kwargs, name, cdata, low, inject_globals), + name=f"ParallelState({name})", + ) + proc.start() ret = { "name": name, "result": None, @@ -2434,7 +2462,7 @@ def call(self, low, chunks=None, running=None, retries=1): ) elif not low.get("__prereq__") and low.get("parallel"): # run the state call in parallel, but only if not in a prereq - ret = self.call_parallel(cdata, low) + ret = self.call_parallel(cdata, low, inject_globals) else: self.format_slots(cdata) with salt.utils.files.set_umask(low.get("__umask__")): diff --git a/salt/states/linux_acl.py b/salt/states/linux_acl.py index 61429b592cfd..f6db6e889362 100644 --- a/salt/states/linux_acl.py +++ b/salt/states/linux_acl.py @@ -708,9 +708,6 @@ def list_absent(name, acl_type, acl_names=None, recurse=False): acl_names The list of users or groups - perms - Remove the permissions eg.: rwx - recurse Set the permissions recursive in the path diff --git a/salt/utils/path.py b/salt/utils/path.py index eec79d7ef7ba..81fb64e67342 100644 --- a/salt/utils/path.py +++ b/salt/utils/path.py @@ -203,7 +203,7 @@ def is_executable(path, membership=res): # now to search through our system_path for path in system_path: - p = join(path, exe) + p = join(os.path.expandvars(path), exe) # iterate through all extensions to see which one is executable for ext in pathext: diff --git a/salt/utils/saltclass.py b/salt/utils/saltclass.py index 7d6fec7c578d..c18d01af3d1e 100644 --- a/salt/utils/saltclass.py +++ b/salt/utils/saltclass.py @@ -1,3 +1,4 @@ +import copy import glob import logging import os @@ -5,6 +6,7 @@ from jinja2 import Environment, FileSystemLoader +import salt.utils.odict import salt.utils.path import salt.utils.yaml @@ -277,9 +279,27 @@ def expand_classes_glob(classes, salt_data): return expanded_classes -def expand_classes_in_order( - minion_dict, salt_data, seen_classes, expanded_classes, classes_to_expand -): +def expand_classes_in_order(minion_dict, salt_data, seen_classes, classes_to_expand): + """ + Expand the list of `classes_to_expand` and return them in the order found + + The return order is `[C, B, A, M, B, L, MINION_ID]` when: + + - minion node include classes `A` and `L` + - `A` include class `B` + - `B` include class `C` + - `L` include class `M` and `B` + + :param dict minion_dict: definition of minion node + :param dict salt_data: configuration data + :param iterable(str) seen_classes: classes already processed + :param iterable(str) classes_to_expand: classes to recursivly expand + :return: Expanded classes in proper order + :rtype: salt.utils.odict.OrderedDict + """ + + expanded_classes = salt.utils.odict.OrderedDict() + # Get classes to expand from minion dictionary if not classes_to_expand and "classes" in minion_dict: classes_to_expand = minion_dict["classes"] @@ -290,71 +310,37 @@ def expand_classes_in_order( for klass in classes_to_expand: if klass not in seen_classes: seen_classes.append(klass) - expanded_classes[klass] = get_class(klass, salt_data) + klass_dict = salt.utils.odict.OrderedDict( + {klass: get_class(klass, salt_data)} + ) # Fix corner case where class is loaded but doesn't contain anything - if expanded_classes[klass] is None: - expanded_classes[klass] = {} + if klass_dict[klass] is None: + klass_dict[klass] = {} # Merge newly found pillars into existing ones - new_pillars = expanded_classes[klass].get("pillars", {}) + new_pillars = klass_dict[klass].get("pillars", {}) if new_pillars: dict_merge(salt_data["__pillar__"], new_pillars) - # Now replace class element in classes_to_expand by expansion - if expanded_classes[klass].get("classes"): - l_id = classes_to_expand.index(klass) - classes_to_expand[l_id:l_id] = expanded_classes[klass]["classes"] - expand_classes_in_order( - minion_dict, - salt_data, - seen_classes, - expanded_classes, - classes_to_expand, - ) - else: - expand_classes_in_order( - minion_dict, + if "classes" in klass_dict[klass]: + nested_classes = expand_classes_in_order( + {}, salt_data, seen_classes, - expanded_classes, - classes_to_expand, + klass_dict[klass].get("classes", {}), ) - # We may have duplicates here and we want to remove them - tmp = [] - for t_element in classes_to_expand: - if t_element not in tmp: - tmp.append(t_element) - - classes_to_expand = tmp - - # Now that we've retrieved every class in order, - # let's return an ordered list of dicts - ord_expanded_classes = [] - ord_expanded_states = [] - for ord_klass in classes_to_expand: - ord_expanded_classes.append(expanded_classes[ord_klass]) - # And be smart and sort out states list - # Address the corner case where states is empty in a class definition - if ( - "states" in expanded_classes[ord_klass] - and expanded_classes[ord_klass]["states"] is None - ): - expanded_classes[ord_klass]["states"] = {} - - if "states" in expanded_classes[ord_klass]: - ord_expanded_states.extend(expanded_classes[ord_klass]["states"]) + # Put current class after nested classes + klass_dict.update(nested_classes) + klass_dict.move_to_end(klass) - # Add our minion dict as final element but check if we have states to process - if "states" in minion_dict and minion_dict["states"] is None: - minion_dict["states"] = [] + expanded_classes.update(klass_dict) - if "states" in minion_dict: - ord_expanded_states.extend(minion_dict["states"]) + # Minion dict must be at the end + if minion_dict: + expanded_classes.update({salt_data["minion_id"]: minion_dict}) - ord_expanded_classes.append(minion_dict) - - return ord_expanded_classes, classes_to_expand, ord_expanded_states + return expanded_classes def expanded_dict_from_minion(minion_id, salt_data): @@ -377,22 +363,35 @@ def expanded_dict_from_minion(minion_id, salt_data): node_dict[minion_id] = {} # Merge newly found pillars into existing ones - dict_merge(salt_data["__pillar__"], node_dict[minion_id].get("pillars", {})) + dict_merge( + salt_data["__pillar__"], copy.deepcopy(node_dict[minion_id]).get("pillars", {}) + ) # Get 2 ordered lists: # expanded_classes: A list of all the dicts # classes_list: List of all the classes - expanded_classes, classes_list, states_list = expand_classes_in_order( - node_dict[minion_id], salt_data, [], {}, [] - ) + expanded_classes = expand_classes_in_order(node_dict[minion_id], salt_data, [], []) # Here merge the pillars together pillars_dict = {} - for exp_dict in expanded_classes: + states_list = [] + classes_list = list(expanded_classes.keys())[:-1] + classes_values = list(expanded_classes.values()) + for exp_dict in classes_values: if "pillars" in exp_dict: dict_merge(pillars_dict, exp_dict) - - return expanded_classes, pillars_dict, classes_list, states_list + if "states" in exp_dict: + states_list.extend(exp_dict["states"]) + + # Avoid duplicates, keep first + state_seen = set() + states_list = [ + state + for state in states_list + if not (state in state_seen or state_seen.add(state)) + ] + + return classes_values, pillars_dict, classes_list, states_list def get_pillars(minion_id, salt_data): diff --git a/tests/pytests/functional/modules/state/test_state.py b/tests/pytests/functional/modules/state/test_state.py index e5e104ffcca0..26928962e7ad 100644 --- a/tests/pytests/functional/modules/state/test_state.py +++ b/tests/pytests/functional/modules/state/test_state.py @@ -3,6 +3,7 @@ import textwrap import threading import time +from textwrap import dedent import pytest @@ -1081,3 +1082,97 @@ def test_state_sls_mock_ret(state_tree): ret["cmd_|-echo1_|-echo 'This is a test!'_|-run"]["comment"] == "Not called, mocked" ) + + +@pytest.fixture +def _state_requires_env(loaders, state_tree): + mod_contents = dedent( + r""" + def test_it(name): + return { + "name": name, + "result": __env__ == "base", + "comment": "", + "changes": {}, + } + """ + ) + sls = "test_spawning" + sls_contents = dedent( + """ + This should not fail on spawning platforms: + requires_env.test_it: + - name: foo + - parallel: true + """ + ) + with pytest.helpers.temp_file( + f"{sls}.sls", sls_contents, state_tree + ), pytest.helpers.temp_file("_states/requires_env.py", mod_contents, state_tree): + res = loaders.modules.saltutil.sync_states() + assert "states.requires_env" in res + yield sls + + +def test_state_apply_parallel_spawning_with_global_dunders(state, _state_requires_env): + """ + Ensure state modules called via `parallel: true` have access to injected + global dunders like `__env__`. + """ + ret = state.apply(_state_requires_env) + assert ( + ret[ + "requires_env_|-This should not fail on spawning platforms_|-foo_|-test_it" + ]["result"] + is True + ) + + +@pytest.fixture +def _state_unpicklable_ctx(loaders, state_tree): + mod_contents = dedent( + r""" + import threading + + class Unpicklable: + def __init__(self): + self._lock = threading.RLock() + + def test_it(): + __context__["booh"] = Unpicklable() + """ + ) + sls = "test_spawning_unpicklable" + sls_contents = dedent( + r""" + {%- do salt["unpicklable.test_it"]() %} + + This should not fail on spawning platforms: + test.nop: + - name: foo + - parallel: true + """ + ) + with pytest.helpers.temp_file( + f"{sls}.sls", sls_contents, state_tree + ), pytest.helpers.temp_file("_modules/unpicklable.py", mod_contents, state_tree): + res = loaders.modules.saltutil.sync_modules() + assert "modules.unpicklable" in res + yield sls + + +@pytest.mark.skip_unless_on_spawning_platform( + reason="Pickling is only relevant on spawning platforms" +) +def test_state_apply_parallel_spawning_with_unpicklable_context( + state, _state_unpicklable_ctx +): + """ + Ensure that if the __context__ dictionary contains unpicklable objects, + they are filtered out instead of causing a crash. + """ + ret = state.apply(_state_unpicklable_ctx) + assert ( + ret["test_|-This should not fail on spawning platforms_|-foo_|-nop"]["result"] + is True + ) diff --git a/tests/pytests/unit/grains/test_disks.py b/tests/pytests/unit/grains/test_disks.py index a0d6d1030e7c..b2a2d5fbd2f1 100644 --- a/tests/pytests/unit/grains/test_disks.py +++ b/tests/pytests/unit/grains/test_disks.py @@ -2,8 +2,6 @@ :codeauthor: :email:`Shane Lee ` """ -import textwrap - import pytest import salt.grains.disks as disks @@ -17,63 +15,58 @@ def configure_loader_modules(): } -def test__windows_disks(): +def test__windows_disks_dict(): """ - Test grains._windows_disks, normal return - Should return a populated dictionary + Test grains._windows_disks with a single disk returned as a dict + Should return 1 disk and no ssds + """ + devices = {"DeviceID": 0, "MediaType": "HDD"} + mock_powershell = MagicMock(return_value=devices) + + with patch.dict(disks.__salt__, {"cmd.powershell": mock_powershell}): + result = disks._windows_disks() + expected = {"disks": ["\\\\.\\PhysicalDrive0"], "ssds": []} + assert result == expected + + +def test__windows_disks_list(): """ - mock_which = MagicMock(return_value="C:\\Windows\\System32\\wbem\\WMIC.exe") - wmic_result = textwrap.dedent( - """ - DeviceId MediaType - 0 4 - 1 0 - 2 3 - 3 5 + test grains._windows_disks with multiple disks and types as a list of dicts + Should return 4 disks and 1 ssd """ - ) - mock_run_all = MagicMock(return_value={"stdout": wmic_result, "retcode": 0}) + devices = [ + {"DeviceID": 0, "MediaType": "SSD"}, + {"DeviceID": 1, "MediaType": "HDD"}, + {"DeviceID": 2, "MediaType": "HDD"}, + {"DeviceID": 3, "MediaType": "HDD"}, + ] + mock_powershell = MagicMock(return_value=devices) - with patch("salt.utils.path.which", mock_which), patch.dict( - disks.__salt__, {"cmd.run_all": mock_run_all} - ): + with patch.dict(disks.__salt__, {"cmd.powershell": mock_powershell}): result = disks._windows_disks() expected = { - "ssds": ["\\\\.\\PhysicalDrive0"], "disks": [ "\\\\.\\PhysicalDrive0", "\\\\.\\PhysicalDrive1", "\\\\.\\PhysicalDrive2", "\\\\.\\PhysicalDrive3", ], + "ssds": ["\\\\.\\PhysicalDrive0"], } assert result == expected - cmd = " ".join( - [ - "C:\\Windows\\System32\\wbem\\WMIC.exe", - "/namespace:\\\\root\\microsoft\\windows\\storage", - "path", - "MSFT_PhysicalDisk", - "get", - "DeviceID,MediaType", - "/format:table", - ] - ) - mock_run_all.assert_called_once_with(cmd) - - -def test__windows_disks_retcode(): + + +def test__windows_disks_empty(): """ - Test grains._windows_disks, retcode 1 + Test grains._windows_disks when nothing is returned Should return empty lists """ - mock_which = MagicMock(return_value="C:\\Windows\\System32\\wbem\\WMIC.exe") - mock_run_all = MagicMock(return_value={"stdout": "", "retcode": 1}) - with patch("salt.utils.path.which", mock_which), patch.dict( - disks.__salt__, {"cmd.run_all": mock_run_all} - ): + devices = {} + mock_powershell = MagicMock(return_value=devices) + + with patch.dict(disks.__salt__, {"cmd.powershell": mock_powershell}): + expected = {"disks": [], "ssds": []} result = disks._windows_disks() - expected = {"ssds": [], "disks": []} assert result == expected diff --git a/tests/pytests/unit/grains/test_iscsi.py b/tests/pytests/unit/grains/test_iscsi.py index 006c1bb4e8bf..615416c19d00 100644 --- a/tests/pytests/unit/grains/test_iscsi.py +++ b/tests/pytests/unit/grains/test_iscsi.py @@ -9,16 +9,39 @@ from tests.support.mock import MagicMock, mock_open, patch -def test_windows_iscsi_iqn_grains(): - cmd_run_mock = MagicMock( - return_value={"stdout": "iSCSINodeName\niqn.1991-05.com.microsoft:simon-x1\n"} - ) - _grains = {} - with patch("salt.utils.path.which", MagicMock(return_value=True)): - with patch("salt.modules.cmdmod.run_all", cmd_run_mock): - _grains["iscsi_iqn"] = iscsi._windows_iqn() - - assert _grains.get("iscsi_iqn") == ["iqn.1991-05.com.microsoft:simon-x1"] +def test_windows_iscsi_iqn_grains_empty(): + nodes_dict = {} + cmd_powershell_mock = MagicMock(return_value=nodes_dict) + with patch("salt.modules.cmdmod.powershell", cmd_powershell_mock): + result = iscsi._windows_iqn() + expected = [] + assert result == expected + + +def test_windows_iscsi_iqn_grains_single(): + nodes_dict = {"NodeAddress": "iqn.1991-05.com.microsoft:simon-x1"} + cmd_powershell_mock = MagicMock(return_value=nodes_dict) + with patch("salt.modules.cmdmod.powershell", cmd_powershell_mock): + result = iscsi._windows_iqn() + expected = ["iqn.1991-05.com.microsoft:simon-x1"] + assert result == expected + + +def test_windows_iscsi_iqn_grains_multiple(): + nodes_list = [ + {"NodeAddress": "iqn.1991-05.com.microsoft:simon-x1"}, + {"NodeAddress": "iqn.1991-05.com.microsoft:simon-x2"}, + {"NodeAddress": "iqn.1991-05.com.microsoft:simon-x3"}, + ] + cmd_powershell_mock = MagicMock(return_value=nodes_list) + with patch("salt.modules.cmdmod.powershell", cmd_powershell_mock): + result = iscsi._windows_iqn() + expected = [ + "iqn.1991-05.com.microsoft:simon-x1", + "iqn.1991-05.com.microsoft:simon-x2", + "iqn.1991-05.com.microsoft:simon-x3", + ] + assert result == expected def test_aix_iscsi_iqn_grains(): diff --git a/tests/pytests/unit/modules/test_selinux.py b/tests/pytests/unit/modules/test_selinux.py index b67a1b525774..0ceba06a1341 100644 --- a/tests/pytests/unit/modules/test_selinux.py +++ b/tests/pytests/unit/modules/test_selinux.py @@ -6,6 +6,8 @@ from salt.exceptions import SaltInvocationError from tests.support.mock import MagicMock, mock_open, patch +pytestmark = [pytest.mark.skip_unless_on_linux] + @pytest.fixture def configure_loader_modules(): @@ -399,7 +401,39 @@ def test_selinux_add_policy_regex(name, sel_type): ): selinux.fcontext_add_policy(name, sel_type=sel_type) filespec = re.escape(name) - expected_cmd_shell = f"semanage fcontext -l | egrep '{filespec}'" + expected_cmd_shell = f"semanage fcontext -l | grep -E '{filespec} '" + mock_cmd_shell.assert_called_once_with( + expected_cmd_shell, + ignore_retcode=True, + ) + expected_cmd_run_all = ( + f"semanage fcontext --modify --type {sel_type} {filespec}" + ) + mock_cmd_run_all.assert_called_once_with( + expected_cmd_run_all, + ) + + +@pytest.mark.parametrize( + "name,sel_type", + ( + ("/usr/share/munin/plugins/mysql_queries", "services_munin_plugin_exec_t"), + ("/usr/share/munin/plugins/mysql_", "unconfined_munin_plugin_exec_t"), + ), +) +def test_selinux_add_policy_shorter_path(name, sel_type): + """ + Test adding policy with a shorter path than an existing entry + """ + mock_cmd_shell = MagicMock(return_value={"retcode": 0}) + mock_cmd_run_all = MagicMock(return_value={"retcode": 0}) + + with patch.dict(selinux.__salt__, {"cmd.shell": mock_cmd_shell}), patch.dict( + selinux.__salt__, {"cmd.run_all": mock_cmd_run_all} + ): + selinux.fcontext_add_policy(name, sel_type=sel_type) + filespec = re.escape(name) + expected_cmd_shell = f"semanage fcontext -l | grep -E '{filespec} '" mock_cmd_shell.assert_called_once_with( expected_cmd_shell, ignore_retcode=True, diff --git a/tests/pytests/unit/modules/test_yumpkg.py b/tests/pytests/unit/modules/test_yumpkg.py index ecdad27ac836..179f3113d552 100644 --- a/tests/pytests/unit/modules/test_yumpkg.py +++ b/tests/pytests/unit/modules/test_yumpkg.py @@ -1428,10 +1428,8 @@ def test_install_with_options(): "--disablerepo=*", "--enablerepo=good", "--branch=foo", - "--setopt", - "obsoletes=0", - "--setopt", - "plugins=0", + "--setopt=obsoletes=0", + "--setopt=plugins=0", "install", "foo", ], @@ -1459,10 +1457,8 @@ def test_install_with_options(): "--disablerepo=bad", "--enablerepo=good", "--branch=foo", - "--setopt", - "obsoletes=0", - "--setopt", - "plugins=0", + "--setopt=obsoletes=0", + "--setopt=plugins=0", "install", "foo", ], @@ -1857,10 +1853,8 @@ def test_upgrade_with_options(): "--disablerepo=*", "--enablerepo=good", "--branch=foo", - "--setopt", - "obsoletes=0", - "--setopt", - "plugins=0", + "--setopt=obsoletes=0", + "--setopt=plugins=0", "--exclude=kernel*", "--nogpgcheck", "upgrade", @@ -1902,10 +1896,8 @@ def test_upgrade_with_options(): "--disablerepo=bad", "--enablerepo=good", "--branch=foo", - "--setopt", - "obsoletes=0", - "--setopt", - "plugins=0", + "--setopt=obsoletes=0", + "--setopt=plugins=0", "--exclude=kernel*", "upgrade", ], @@ -3051,10 +3043,8 @@ def test_pkg_update_dnf(): "dnf", "--quiet", "-y", - "--setopt", - "plugins=0", - "--setopt", - "obsoletes=False", + "--setopt=plugins=0", + "--setopt=obsoletes=False", "upgrade", "foo", ], diff --git a/tests/pytests/unit/pillar/test_saltclass.py b/tests/pytests/unit/pillar/test_saltclass.py index b96db78bc49a..f4bb0bcbd577 100644 --- a/tests/pytests/unit/pillar/test_saltclass.py +++ b/tests/pytests/unit/pillar/test_saltclass.py @@ -23,6 +23,8 @@ def temp_saltclass_tree(tmp_path, minion_id): nodes_dir.mkdir(parents=True, exist_ok=True) default_dir = classes_dir / "default" default_dir.mkdir(parents=True, exist_ok=True) + users_dir = default_dir / "users" + users_dir.mkdir(parents=True, exist_ok=True) roles_dir = classes_dir / "roles" roles_dir.mkdir(parents=True, exist_ok=True) nginx_subdir = roles_dir / "nginx" @@ -35,6 +37,9 @@ def temp_saltclass_tree(tmp_path, minion_id): - default.motd - default.empty + states: + - default + pillars: default: network: @@ -50,6 +55,13 @@ def temp_saltclass_tree(tmp_path, minion_id): test_list: - a: ${default:network:ntp:srv1} - ${default:network:ntp:srv2} + + global_scalar: from default + test_dict: + a_scalar: from default + a_list: + - element1 + - element2 """ default_init.write_text(test_list) @@ -57,24 +69,91 @@ def temp_saltclass_tree(tmp_path, minion_id): nodes_text = """ environment: base + states: + - minion_node + classes: {% for class in ['default', 'roles.*', 'empty.*'] %} - {{ class }} {% endfor %} + + pillars: + global_scalar: from minion_node + test_dict: + a_scalar: from minion_node """ minion_node_file.write_text(nodes_text) - (default_dir / "users.yml").write_text("test: this is a test") + (users_dir / "init.yml").write_text( + """ + classes: + - default.users.foo + + states: + - users + + pillars: + default: + ntp: + srv1: 192.168.20.10 + + global_scalar: from users + test_dict: + a_scalar: from users + """ + ) + (users_dir / "foo.yml").write_text( + """ + states: + - users.foo + + global_scalar: from users.foo + users_foo_scalar: from users.foo + test_dict: + a_scalar: from users.foo + """ + ) (default_dir / "empty.yml").write_text("test: this is a test") - (default_dir / "motd.yml").write_text("test: this is a test") - (roles_dir / "app.yml").write_text("test: this is a test") - (nginx_subdir / "init.yml").write_text("test: this is a test") + (default_dir / "motd.yml").write_text( + """ + states: + - motd + + pillars: + global_scalar: from motd + """ + ) + (roles_dir / "app.yml").write_text( + """ + states: + - app + + pillars: + global_scalar: from app + """ + ) + (nginx_subdir / "init.yml").write_text( + """ + states: + - nginx + + pillars: + global_scalar: from nginx + nginx_scalar: from nginx + """ + ) return dirname -def test_succeeds(temp_saltclass_tree): +def test_classes_order(temp_saltclass_tree): + """ + Classes must be correctly ordered. + + See: https://github.com/saltstack/salt/issues/58969 + """ expected_ret = [ + "default.users.foo", "default.users", "default.motd", "default.empty", @@ -110,3 +189,102 @@ def test_list_expansion_succeeds(temp_saltclass_tree): pytest.fail(err) # Else give the parsed content result assert expected_ret == parsed_ret + + +def test_node_override_classes_scalars(temp_saltclass_tree): + """ + Scalars pillars defined in a node definition must override the + definition from classes. + """ + expected_ret = "from minion_node" + fake_args = {"path": str(temp_saltclass_tree)} + fake_pillar = {} + fake_minion_id = "fake_id" + try: + full_ret = saltclass.ext_pillar(fake_minion_id, fake_pillar, fake_args) + parsed_ret = full_ret["global_scalar"] + # Fail the test if we hit our NoneType error + except TypeError as err: + pytest.fail(err) + # Else give the parsed content result + assert expected_ret == parsed_ret + + +def test_node_override_classes_scalar_in_dict(temp_saltclass_tree): + """ + Scalars defined in `dict` pillars defined in a node definition must override the + same dict definition from classes. + + See: https://github.com/saltstack/salt/issues/63933 + """ + expected_ret = "from minion_node" + fake_args = {"path": str(temp_saltclass_tree)} + fake_pillar = {} + fake_minion_id = "fake_id" + try: + full_ret = saltclass.ext_pillar(fake_minion_id, fake_pillar, fake_args) + parsed_ret = full_ret["test_dict"]["a_scalar"] + # Fail the test if we hit our NoneType error + except TypeError as err: + pytest.fail(err) + # Else give the parsed content result + assert expected_ret == parsed_ret + + +def test_node_override_classes_list_in_dict(temp_saltclass_tree): + """ + `list` under a `dict` defined in a node definition must override the + same definition from classes. + + See: https://github.com/saltstack/salt/issues/63933 + """ + expected_ret = {"srv1": "192.168.10.10", "srv2": "192.168.10.20"} + fake_args = {"path": str(temp_saltclass_tree)} + fake_pillar = {} + fake_minion_id = "fake_id" + try: + full_ret = saltclass.ext_pillar(fake_minion_id, fake_pillar, fake_args) + parsed_ret = full_ret["default"]["network"]["ntp"] + # Fail the test if we hit our NoneType error + except TypeError as err: + pytest.fail(err) + # Else give the parsed content result + assert expected_ret == parsed_ret + + +def test_list_in_dict_no_duplication(temp_saltclass_tree): + """ + `list` under a `dict` in pillar must not be duplicated. + + See: + """ + expected_ret = ["element1", "element2"] + fake_args = {"path": str(temp_saltclass_tree)} + fake_pillar = {} + fake_minion_id = "fake_id" + try: + full_ret = saltclass.ext_pillar(fake_minion_id, fake_pillar, fake_args) + parsed_ret = full_ret["test_dict"]["a_list"] + # Fail the test if we hit our NoneType error + except TypeError as err: + pytest.fail(err) + # Else give the parsed content result + assert expected_ret == parsed_ret + + +def test_nested_classes_has_pillars(temp_saltclass_tree): + """ + pillars defined in nested classes are present + """ + expected_ret = "from nginx" + fake_args = {"path": str(temp_saltclass_tree)} + fake_pillar = {} + fake_minion_id = "fake_id" + try: + full_ret = saltclass.ext_pillar(fake_minion_id, fake_pillar, fake_args) + parsed_ret = full_ret["nginx_scalar"] + # Fail the test if we hit our NoneType error + except TypeError as err: + pytest.fail(err) + # Else give the parsed content result + assert expected_ret == parsed_ret diff --git a/tools/precommit/docstrings.py b/tools/precommit/docstrings.py index fa1d435833b7..52d6bd7f9612 100644 --- a/tools/precommit/docstrings.py +++ b/tools/precommit/docstrings.py @@ -345,7 +345,6 @@ "get_pillars", "expand_variables", "render_jinja", - "expand_classes_in_order", "dict_search_and_replace", "expanded_dict_from_minion", "find_value_to_expand",