Skip to content

Commit

Permalink
Merge pull request saltstack#63030 from twangboy/cloud_paths
Browse files Browse the repository at this point in the history
Fix old root_dir paths on Windows
  • Loading branch information
garethgreenaway authored Nov 14, 2022
2 parents b7f9c2f + f58d00d commit 1e1811e
Show file tree
Hide file tree
Showing 4 changed files with 211 additions and 14 deletions.
1 change: 1 addition & 0 deletions changelog/62968.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Issue #62968: Fix issue where cloud deployments were putting the keys in the wrong location on Windows hosts
1 change: 1 addition & 0 deletions changelog/63024.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Issue #63024: Fix issue where grains and config data were being place in the wrong location on Windows hosts
36 changes: 22 additions & 14 deletions salt/utils/cloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -956,7 +956,7 @@ def remove_service(self, wait_timeout=10, sleep_wait=1):

def run_winexe_command(cmd, args, host, username, password, port=445):
"""
Run a command remotly via the winexe executable
Run a command remotely via the winexe executable
"""
creds = "-U '{}%{}' //{}".format(username, password, host)
logging_creds = "-U '{}%XXX-REDACTED-XXX' //{}".format(username, host)
Expand All @@ -967,7 +967,7 @@ def run_winexe_command(cmd, args, host, username, password, port=445):

def run_psexec_command(cmd, args, host, username, password, port=445):
"""
Run a command remotly using the psexec protocol
Run a command remotely using the psexec protocol
"""
service_name = "PS-Exec-{}".format(uuid.uuid4())
stdout, stderr, ret_code = "", "", None
Expand Down Expand Up @@ -1117,7 +1117,7 @@ def validate_windows_cred(
"cmd.exe", "/c hostname", host, username, password, port=445
)
except Exception as exc: # pylint: disable=broad-except
log.exception("Exceoption while executing psexec")
log.exception("Exception while executing psexec")
if ret_code == 0:
break
time.sleep(retry_delay)
Expand Down Expand Up @@ -1285,16 +1285,22 @@ def deploy_windows(
return False

salt.utils.smb.mkdirs("salttemp", conn=smb_conn)
salt.utils.smb.mkdirs("salt/conf/pki/minion", conn=smb_conn)
root_dir = "ProgramData/Salt Project/Salt"
salt.utils.smb.mkdirs("{}/conf/pki/minion".format(root_dir), conn=smb_conn)
root_dir = "ProgramData\\Salt Project\\Salt"

if minion_pub:
salt.utils.smb.put_str(
minion_pub, "salt\\conf\\pki\\minion\\minion.pub", conn=smb_conn
minion_pub,
"{}\\conf\\pki\\minion\\minion.pub".format(root_dir),
conn=smb_conn,
)

if minion_pem:
salt.utils.smb.put_str(
minion_pem, "salt\\conf\\pki\\minion\\minion.pem", conn=smb_conn
minion_pem,
"{}\\conf\\pki\\minion\\minion.pem".format(root_dir),
conn=smb_conn,
)

if master_sign_pub_file:
Expand All @@ -1305,8 +1311,7 @@ def deploy_windows(
try:
salt.utils.smb.put_file(
master_sign_pub_file,
"salt\\conf\\pki\\minion\\master_sign.pub",
"C$",
"{}\\conf\\pki\\minion\\master_sign.pub".format(root_dir),
conn=smb_conn,
)
except Exception as e: # pylint: disable=broad-except
Expand Down Expand Up @@ -1359,21 +1364,24 @@ def deploy_windows(
if minion_grains:
salt.utils.smb.put_str(
salt_config_to_yaml(minion_grains, line_break="\r\n"),
"salt\\conf\\grains",
"{}\\conf\\grains".format(root_dir),
conn=smb_conn,
)
# Add special windows minion configuration
# that must be in the minion config file
windows_minion_conf = {
"ipc_mode": "tcp",
"root_dir": "c:\\salt",
"pki_dir": "/conf/pki/minion",
"multiprocessing": False,
"ipc_mode": minion_conf.pop("ipc_mode", "tcp"),
"pki_dir": minion_conf.pop("pki_dir", "/conf/pki/minion"),
"multiprocessing": minion_conf.pop("multiprocessing", True),
}
if master and "master" not in minion_conf:
windows_minion_conf["master"] = master
if name and "id" not in minion_conf:
windows_minion_conf["id"] = name
minion_conf = dict(minion_conf, **windows_minion_conf)
salt.utils.smb.put_str(
salt_config_to_yaml(minion_conf, line_break="\r\n"),
"salt\\conf\\minion",
"{}\\conf\\minion".format(root_dir),
conn=smb_conn,
)
# Delete C:\salttmp\ and installer file
Expand Down
187 changes: 187 additions & 0 deletions tests/pytests/unit/utils/test_cloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,193 @@ def test_deploy_windows_custom_port():
mock.assert_called_once_with("test", "Administrator", None, 1234)


@pytest.mark.skip_unless_on_windows(reason="Only applicable for Windows.")
def test_deploy_windows_programdata():
"""
Test deploy_windows with a custom port
"""
mock_true = MagicMock(return_value=True)
mock_tuple = MagicMock(return_value=(0, 0, 0))
mock_conn = MagicMock()

with patch("salt.utils.smb", MagicMock()) as mock_smb:
mock_smb.get_conn.return_value = mock_conn
mock_smb.mkdirs.return_value = None
mock_smb.put_file.return_value = None
mock_smb.delete_file.return_value = None
mock_smb.delete_directory.return_value = None
with 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
):
cloud.deploy_windows(host="test", win_installer="")
expected = "ProgramData/Salt Project/Salt/conf/pki/minion"
mock_smb.mkdirs.assert_called_with(expected, conn=mock_conn)


@pytest.mark.skip_unless_on_windows(reason="Only applicable for Windows.")
def test_deploy_windows_programdata_minion_pub():
"""
Test deploy_windows with a custom port
"""
mock_true = MagicMock(return_value=True)
mock_tuple = MagicMock(return_value=(0, 0, 0))
mock_conn = MagicMock()

with patch("salt.utils.smb", MagicMock()) as mock_smb:
mock_smb.get_conn.return_value = mock_conn
mock_smb.mkdirs.return_value = None
mock_smb.put_file.return_value = None
mock_smb.put_str.return_value = None
mock_smb.delete_file.return_value = None
mock_smb.delete_directory.return_value = None
with 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
):
cloud.deploy_windows(host="test", minion_pub="pub", win_installer="")
expected = "ProgramData\\Salt Project\\Salt\\conf\\pki\\minion\\minion.pub"
mock_smb.put_str.assert_called_with("pub", expected, conn=mock_conn)


@pytest.mark.skip_unless_on_windows(reason="Only applicable for Windows.")
def test_deploy_windows_programdata_minion_pem():
"""
Test deploy_windows with a custom port
"""
mock_true = MagicMock(return_value=True)
mock_tuple = MagicMock(return_value=(0, 0, 0))
mock_conn = MagicMock()

with patch("salt.utils.smb", MagicMock()) as mock_smb:
mock_smb.get_conn.return_value = mock_conn
mock_smb.mkdirs.return_value = None
mock_smb.put_file.return_value = None
mock_smb.put_str.return_value = None
mock_smb.delete_file.return_value = None
mock_smb.delete_directory.return_value = None
with 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
):
cloud.deploy_windows(host="test", minion_pem="pem", win_installer="")
expected = "ProgramData\\Salt Project\\Salt\\conf\\pki\\minion\\minion.pem"
mock_smb.put_str.assert_called_with("pem", expected, conn=mock_conn)


@pytest.mark.skip_unless_on_windows(reason="Only applicable for Windows.")
def test_deploy_windows_programdata_master_sign_pub_file():
"""
Test deploy_windows with a custom port
"""
mock_true = MagicMock(return_value=True)
mock_tuple = MagicMock(return_value=(0, 0, 0))
mock_conn = MagicMock()

with patch("salt.utils.smb", MagicMock()) as mock_smb:
mock_smb.get_conn.return_value = mock_conn
mock_smb.mkdirs.return_value = None
mock_smb.put_file.return_value = None
mock_smb.put_str.return_value = None
mock_smb.delete_file.return_value = None
mock_smb.delete_directory.return_value = None
with 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
):
cloud.deploy_windows(
host="test", master_sign_pub_file="test.txt", win_installer=""
)
expected = (
"ProgramData\\Salt Project\\Salt\\conf\\pki\\minion\\master_sign.pub"
)
called = False
for call in mock_smb.put_file.mock_calls:
if expected in call[1]:
called = True
assert called


@pytest.mark.skip_unless_on_windows(reason="Only applicable for Windows.")
def test_deploy_windows_programdata_minion_conf_grains():
"""
Test deploy_windows with a custom port
"""
mock_true = MagicMock(return_value=True)
mock_tuple = MagicMock(return_value=(0, 0, 0))
mock_conn = MagicMock()

with patch("salt.utils.smb", MagicMock()) as mock_smb:
mock_smb.get_conn.return_value = mock_conn
mock_smb.mkdirs.return_value = None
mock_smb.put_file.return_value = None
mock_smb.put_str.return_value = None
mock_smb.delete_file.return_value = None
mock_smb.delete_directory.return_value = None
with 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
):
minion_conf = {"grains": {"spongebob": "squarepants"}}
cloud.deploy_windows(host="test", minion_conf=minion_conf, win_installer="")
expected = "ProgramData\\Salt Project\\Salt\\conf\\grains"
called = False
for call in mock_smb.put_str.mock_calls:
if expected in call[1]:
called = True
assert called


@pytest.mark.skip_unless_on_windows(reason="Only applicable for Windows.")
def test_deploy_windows_programdata_minion_conf():
"""
Test deploy_windows with a custom port
"""
mock_true = MagicMock(return_value=True)
mock_tuple = MagicMock(return_value=(0, 0, 0))
mock_conn = MagicMock()

with patch("salt.utils.smb", MagicMock()) as mock_smb:
mock_smb.get_conn.return_value = mock_conn
mock_smb.mkdirs.return_value = None
mock_smb.put_file.return_value = None
mock_smb.put_str.return_value = None
mock_smb.delete_file.return_value = None
mock_smb.delete_directory.return_value = None
with 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
):
minion_conf = {"master": "test-master"}
cloud.deploy_windows(host="test", minion_conf=minion_conf, win_installer="")
config = (
"ipc_mode: tcp\r\n"
"master: test-master\r\n"
"multiprocessing: true\r\n"
"pki_dir: /conf/pki/minion\r\n"
)
expected = "ProgramData\\Salt Project\\Salt\\conf\\minion"
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():
"""
Expand Down

0 comments on commit 1e1811e

Please sign in to comment.