From 99068faa223611ff3fde280f4794680c105c5477 Mon Sep 17 00:00:00 2001 From: lubinatien <7661774+lubinatien@users.noreply.github.com> Date: Wed, 25 Feb 2026 10:46:52 +0100 Subject: [PATCH 1/3] Fix import for WinRM version check PyPI's package is named pywinrm. The importlib.metadata.version("winrm") call raises PackageNotFoundError which isn't caught by except ImportError, crashing the block and leaving winrm undefined despite being importable. --- salt/utils/cloud.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/utils/cloud.py b/salt/utils/cloud.py index c32bbdbb5346..c6ed74a8c1f9 100644 --- a/salt/utils/cloud.py +++ b/salt/utils/cloud.py @@ -82,7 +82,7 @@ # Verify WinRM 0.3.0 or greater - version = importlib.metadata.version("winrm") + version = importlib.metadata.version("pywinrm") if not salt.utils.versions.compare(version, ">=", WINRM_MIN_VER): HAS_WINRM = False else: From b4b8754f672e4e254929523b1fc357d157b3428a Mon Sep 17 00:00:00 2001 From: lubinatien <7661774+lubinatien@users.noreply.github.com> Date: Thu, 26 Feb 2026 14:26:24 +0100 Subject: [PATCH 2/3] Fix winrm detection bug in salt-cloud --- changelog/68768.fixed.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/68768.fixed.md diff --git a/changelog/68768.fixed.md b/changelog/68768.fixed.md new file mode 100644 index 000000000000..d4462e9fc8d3 --- /dev/null +++ b/changelog/68768.fixed.md @@ -0,0 +1 @@ +Fixed a winrm detection bug in salt-cloud. From 9f2483845439fa583e2e5eeabd51285907bacde6 Mon Sep 17 00:00:00 2001 From: "Daniel A. Wozniak" Date: Sat, 6 Jun 2026 14:55:17 -0700 Subject: [PATCH 3/3] Add cross-platform regression test for pywinrm dist-name lookup The existing test was @skip_unless_on_windows and asserted only on the dist name itself, so it would not have caught the regression where salt/utils/cloud.py passed 'winrm' (module name) instead of 'pywinrm' (distribution name) to importlib.metadata.version(). Drop the platform gate and assert on the production symbol (cloud.HAS_WINRM) so the detection path is pinned on every CI runner with pywinrm installed. --- tests/pytests/unit/utils/test_cloud.py | 42 +++++++++----------------- 1 file changed, 14 insertions(+), 28 deletions(-) diff --git a/tests/pytests/unit/utils/test_cloud.py b/tests/pytests/unit/utils/test_cloud.py index f077b24c4a86..4bd5e54b8670 100644 --- a/tests/pytests/unit/utils/test_cloud.py +++ b/tests/pytests/unit/utils/test_cloud.py @@ -449,40 +449,26 @@ def test_deploy_windows_programdata_minion_conf(): mock_smb.put_str.assert_called_with(config, expected, conn=mock_conn) -@pytest.mark.skip_unless_on_windows(reason="Only applicable for Windows.") def test_winrm_pinnned_version(): """ Test that winrm is pinned to a version 0.3.0 or higher. + + Also asserts that ``salt.utils.cloud.HAS_WINRM`` is True when the + pywinrm distribution is importable. This pins the production + detection path so a regression in the dist-name lookup (the bug + that motivated this test) is caught on every platform CI runs on, + not just Windows. """ - mock_true = MagicMock(return_value=True) - mock_tuple = MagicMock(return_value=(0, 0, 0)) - with patch("salt.utils.smb.get_conn", MagicMock()), patch( - "salt.utils.smb.mkdirs", MagicMock() - ), patch("salt.utils.smb.put_file", MagicMock()), patch( - "salt.utils.smb.delete_file", MagicMock() - ), patch( - "salt.utils.smb.delete_directory", MagicMock() - ), patch( - "time.sleep", MagicMock() - ), patch.object( - cloud, "wait_for_port", mock_true - ), patch.object( - cloud, "fire_event", MagicMock() - ), patch.object( - cloud, "wait_for_psexecsvc", mock_true - ), patch.object( - cloud, "run_psexec_command", mock_tuple - ): + try: + import winrm # pylint: disable=unused-import + except ImportError: + raise pytest.skip('The "winrm" python module is not installed in this env.') - try: - import winrm # pylint: disable=unused-import - except ImportError: - raise pytest.skip('The "winrm" python module is not installed in this env.') - else: - from importlib.metadata import version + from importlib.metadata import version - winrm_version = version("pywinrm") - assert winrm_version >= "0.3.0" + winrm_version = version("pywinrm") + assert winrm_version >= "0.3.0" + assert cloud.HAS_WINRM is True def test_ssh_gateway_arguments_default_alive_args():