mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Add new ssh_pre_flight roster option
This commit is contained in:
parent
6258f6b61b
commit
4fa68c9dce
6 changed files with 409 additions and 112 deletions
|
@ -16,3 +16,31 @@ also support the syntax used in :py:mod:`module.run <salt.states.module.run>`.
|
|||
The old syntax for the mine_function - as a dict, or as a list with dicts that
|
||||
contain more than exactly one key - is still supported but discouraged in favor
|
||||
of the more uniform syntax of module.run.
|
||||
|
||||
Salt-SSH updates
|
||||
================
|
||||
|
||||
A new Salt-SSH roster option `ssh_pre_flight` has been added. This enables you to run a
|
||||
script before Salt-SSH tries to run any commands. You can set this option in the roster
|
||||
for a specific minion or use the `roster_defaults` to set it for all minions.
|
||||
|
||||
Example for setting `ssh_pre_flight` for specific host in roster file
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
minion1:
|
||||
host: localhost
|
||||
user: root
|
||||
passwd: P@ssword
|
||||
ssh_pre_flight: /srv/salt/pre_flight.sh
|
||||
|
||||
Example for setting `ssh_pre_flight` using roster_defaults, so all minions
|
||||
run this script.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
roster_defaults:
|
||||
ssh_pre_flight: /srv/salt/pre_flight.sh
|
||||
|
||||
The `ssh_pre_flight` script will only run if the thin dir is not currently on the
|
||||
minion.
|
||||
|
|
|
@ -61,6 +61,20 @@ The information which can be stored in a roster ``target`` is the following:
|
|||
# components. Defaults to /tmp/salt-<hash>.
|
||||
cmd_umask: # umask to enforce for the salt-call command. Should be in
|
||||
# octal (so for 0o077 in YAML you would do 0077, or 63)
|
||||
ssh_pre_flight: # Path to a script that will run before all other salt-ssh
|
||||
# commands. Will only run the first time when the thin dir
|
||||
# does not exist. Added in Sodium Release.
|
||||
|
||||
.. _ssh_pre_flight:
|
||||
|
||||
ssh_pre_flight
|
||||
--------------
|
||||
|
||||
A Salt-SSH roster option `ssh_pre_flight` was added in the Sodium release. This enables
|
||||
you to run a script before Salt-SSH tries to run any commands. You can set this option
|
||||
in the roster for a specific minion or use the `roster_defaults` to set it for all minions.
|
||||
This script will only run if the thin dir is not currently on the minion. This means it will
|
||||
only run on the first run of salt-ssh or if you have recently wiped out your thin dir.
|
||||
|
||||
.. _roster_defaults:
|
||||
|
||||
|
|
|
@ -895,6 +895,11 @@ class Single(object):
|
|||
self.fsclient = fsclient
|
||||
self.context = {"master_opts": self.opts, "fileclient": self.fsclient}
|
||||
|
||||
self.ssh_pre_flight = kwargs.get('ssh_pre_flight', None)
|
||||
|
||||
if self.ssh_pre_flight:
|
||||
self.ssh_pre_file = os.path.basename(self.ssh_pre_flight)
|
||||
|
||||
if isinstance(argv, six.string_types):
|
||||
self.argv = [argv]
|
||||
else:
|
||||
|
@ -974,6 +979,18 @@ class Single(object):
|
|||
return arg
|
||||
return "".join(["\\" + char if re.match(r"\W", char) else char for char in arg])
|
||||
|
||||
def run_ssh_pre_flight(self):
|
||||
'''
|
||||
Run our pre_flight script before running any ssh commands
|
||||
'''
|
||||
script = os.path.join(tempfile.gettempdir(), self.ssh_pre_file)
|
||||
|
||||
self.shell.send(
|
||||
self.ssh_pre_flight,
|
||||
script)
|
||||
|
||||
return self.execute_script(script)
|
||||
|
||||
def deploy(self):
|
||||
"""
|
||||
Deploy salt-thin
|
||||
|
@ -1008,6 +1025,18 @@ class Single(object):
|
|||
"""
|
||||
stdout = stderr = retcode = None
|
||||
|
||||
if self.ssh_pre_flight:
|
||||
if os.path.exists(self.thin_dir):
|
||||
log.debug('{0} thin dir already exists. Not running ssh_pre_flight script'.format(self.thin_dir))
|
||||
elif not os.path.exists(self.ssh_pre_flight):
|
||||
log.error('The ssh_pre_flight script {0} does not exist'.format(self.ssh_pre_flight))
|
||||
else:
|
||||
stdout, stderr, retcode = self.run_ssh_pre_flight()
|
||||
if stderr:
|
||||
log.error('Error running ssh_pre_flight script {0}'.format(self.ssh_pre_file))
|
||||
return stdout, stderr, retcode
|
||||
log.debug('Successfully ran the ssh_pre_flight script: {0}'.format(self.ssh_pre_file))
|
||||
|
||||
if self.opts.get("raw_shell", False):
|
||||
cmd_str = " ".join([self._escape_arg(arg) for arg in self.argv])
|
||||
stdout, stderr, retcode = self.shell.exec_cmd(cmd_str)
|
||||
|
@ -1263,6 +1292,26 @@ ARGS = {arguments}\n'''.format(
|
|||
|
||||
return cmd
|
||||
|
||||
def execute_script(self, script, extension='py', pre_dir=''):
|
||||
'''
|
||||
execute a script on the minion then delete
|
||||
'''
|
||||
if extension == 'ps1':
|
||||
ret = self.shell.exec_cmd('"powershell {0}"'.format(script))
|
||||
else:
|
||||
if not self.winrm:
|
||||
ret = self.shell.exec_cmd('/bin/sh \'{0}{1}\''.format(pre_dir, script))
|
||||
else:
|
||||
ret = saltwinshell.call_python(self, script)
|
||||
|
||||
# Remove file from target system
|
||||
if not self.winrm:
|
||||
self.shell.exec_cmd('rm \'{0}{1}\''.format(pre_dir, script))
|
||||
else:
|
||||
self.shell.exec_cmd('del {0}'.format(script))
|
||||
|
||||
return ret
|
||||
|
||||
def shim_cmd(self, cmd_str, extension="py"):
|
||||
"""
|
||||
Run a shim command.
|
||||
|
@ -1293,22 +1342,9 @@ ARGS = {arguments}\n'''.format(
|
|||
except IOError:
|
||||
pass
|
||||
|
||||
# Execute shim
|
||||
if extension == "ps1":
|
||||
ret = self.shell.exec_cmd('"powershell {0}"'.format(target_shim_file))
|
||||
else:
|
||||
if not self.winrm:
|
||||
ret = self.shell.exec_cmd(
|
||||
"/bin/sh '$HOME/{0}'".format(target_shim_file)
|
||||
)
|
||||
else:
|
||||
ret = saltwinshell.call_python(self, target_shim_file)
|
||||
|
||||
# Remove shim from target system
|
||||
if not self.winrm:
|
||||
self.shell.exec_cmd("rm '$HOME/{0}'".format(target_shim_file))
|
||||
else:
|
||||
self.shell.exec_cmd("del {0}".format(target_shim_file))
|
||||
ret = self.execute_script(script=target_shim_file,
|
||||
extension=extension,
|
||||
pre_dir='$HOME/')
|
||||
|
||||
return ret
|
||||
|
||||
|
|
|
@ -10,6 +10,10 @@ import shutil
|
|||
|
||||
# Import salt testing libs
|
||||
from tests.support.case import SSHCase
|
||||
from tests.support.runtests import RUNTIME_VARS
|
||||
|
||||
# Import salt libs
|
||||
import salt.utils.yaml
|
||||
|
||||
|
||||
class SSHTest(SSHCase):
|
||||
|
@ -34,6 +38,26 @@ class SSHTest(SSHCase):
|
|||
os.path.exists(os.path.join(thin_dir, "salt-call"))
|
||||
os.path.exists(os.path.join(thin_dir, "running_data"))
|
||||
|
||||
def test_ssh_pre_flight(self):
|
||||
'''
|
||||
test ssh when ssh_pre_flight is set
|
||||
ensure the script runs successfully
|
||||
'''
|
||||
roster = os.path.join(RUNTIME_VARS.TMP, 'pre_flight_roster')
|
||||
|
||||
data = {'ssh_pre_flight': os.path.join(RUNTIME_VARS.TMP, 'ssh_pre_flight.sh')}
|
||||
self.custom_roster(roster, data)
|
||||
|
||||
test_script = os.path.join(RUNTIME_VARS.TMP,
|
||||
'test-pre-flight-script-worked.txt')
|
||||
|
||||
with salt.utils.files.fopen(data['ssh_pre_flight'], 'w') as fp_:
|
||||
fp_.write('touch {0}'.format(test_script))
|
||||
|
||||
ret = self.run_function('test.ping', roster_file=roster)
|
||||
|
||||
assert os.path.exists(test_script)
|
||||
|
||||
def tearDown(self):
|
||||
"""
|
||||
make sure to clean up any old ssh directories
|
||||
|
|
|
@ -27,6 +27,7 @@ import time
|
|||
from datetime import datetime, timedelta
|
||||
|
||||
# Import 3rd-party libs
|
||||
import salt.utils.files
|
||||
from salt.ext import six
|
||||
from salt.ext.six.moves import cStringIO # pylint: disable=import-error
|
||||
from tests.support.cli_scripts import ScriptPathMixin
|
||||
|
@ -83,26 +84,21 @@ class ShellTestCase(TestCase, AdaptedConfigurationTestCaseMixin, ScriptPathMixin
|
|||
timeout=timeout,
|
||||
)
|
||||
|
||||
def run_ssh(
|
||||
self,
|
||||
arg_str,
|
||||
with_retcode=False,
|
||||
timeout=25,
|
||||
catch_stderr=False,
|
||||
wipe=False,
|
||||
raw=False,
|
||||
**kwargs
|
||||
):
|
||||
"""
|
||||
def run_ssh(self, arg_str, with_retcode=False, timeout=25,
|
||||
catch_stderr=False, wipe=False, raw=False, roster_file=None, **kwargs):
|
||||
'''
|
||||
Execute salt-ssh
|
||||
"""
|
||||
arg_str = "{0} {1} -c {2} -i --priv {3} --roster-file {4} localhost {5} --out=json".format(
|
||||
" -W" if wipe else "",
|
||||
" -r" if raw else "",
|
||||
'''
|
||||
if not roster_file:
|
||||
roster_file = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'roster')
|
||||
|
||||
arg_str = '{0} {1} -c {2} -i --priv {3} --roster-file {4} localhost {5} --out=json'.format(
|
||||
' -W' if wipe else '',
|
||||
' -r' if raw else '',
|
||||
self.config_dir,
|
||||
os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "key_test"),
|
||||
os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "roster"),
|
||||
arg_str,
|
||||
os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'key_test'),
|
||||
roster_file,
|
||||
arg_str
|
||||
)
|
||||
return self.run_script(
|
||||
"salt-ssh",
|
||||
|
@ -527,37 +523,29 @@ class ShellCase(ShellTestCase, AdaptedConfigurationTestCaseMixin, ScriptPathMixi
|
|||
log.debug("Result of run_spm for command '%s': %s", arg_str, ret)
|
||||
return ret
|
||||
|
||||
def run_ssh(
|
||||
self,
|
||||
arg_str,
|
||||
with_retcode=False,
|
||||
catch_stderr=False,
|
||||
timeout=RUN_TIMEOUT,
|
||||
wipe=True,
|
||||
raw=False,
|
||||
**kwargs
|
||||
):
|
||||
"""
|
||||
def run_ssh(self, arg_str, with_retcode=False, catch_stderr=False, # pylint: disable=W0221
|
||||
timeout=RUN_TIMEOUT, wipe=True, raw=False, roster_file=None, **kwargs):
|
||||
'''
|
||||
Execute salt-ssh
|
||||
"""
|
||||
arg_str = "{0} -ldebug{1} -c {2} -i --priv {3} --roster-file {4} --out=json localhost {5}".format(
|
||||
" -W" if wipe else "",
|
||||
" -r" if raw else "",
|
||||
'''
|
||||
if not roster_file:
|
||||
roster_file = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'roster')
|
||||
|
||||
arg_str = '{0} -ldebug{1} -c {2} -i --priv {3} --roster-file {4} --out=json localhost {5}'.format(
|
||||
' -W' if wipe else '',
|
||||
' -r' if raw else '',
|
||||
self.config_dir,
|
||||
os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "key_test"),
|
||||
os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "roster"),
|
||||
arg_str,
|
||||
)
|
||||
ret = self.run_script(
|
||||
"salt-ssh",
|
||||
arg_str,
|
||||
with_retcode=with_retcode,
|
||||
catch_stderr=catch_stderr,
|
||||
timeout=timeout,
|
||||
raw=True,
|
||||
**kwargs
|
||||
)
|
||||
log.debug("Result of run_ssh for command '%s %s': %s", arg_str, kwargs, ret)
|
||||
os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'key_test'),
|
||||
roster_file,
|
||||
arg_str)
|
||||
ret = self.run_script('salt-ssh',
|
||||
arg_str,
|
||||
with_retcode=with_retcode,
|
||||
catch_stderr=catch_stderr,
|
||||
timeout=timeout,
|
||||
raw=True,
|
||||
**kwargs)
|
||||
log.debug('Result of run_ssh for command \'%s %s\': %s', arg_str, kwargs, ret)
|
||||
return ret
|
||||
|
||||
# pylint: enable=arguments-differ
|
||||
|
@ -750,9 +738,6 @@ class SPMCase(TestCase, AdaptedConfigurationTestCaseMixin):
|
|||
for f_dir in dirs:
|
||||
os.makedirs(f_dir)
|
||||
|
||||
# Late import
|
||||
import salt.utils.files
|
||||
|
||||
with salt.utils.files.fopen(self.formula_sls, "w") as fp:
|
||||
fp.write(
|
||||
textwrap.dedent(
|
||||
|
@ -807,7 +792,6 @@ class SPMCase(TestCase, AdaptedConfigurationTestCaseMixin):
|
|||
}
|
||||
)
|
||||
|
||||
import salt.utils.files
|
||||
import salt.utils.yaml
|
||||
|
||||
if not os.path.isdir(config["formula_path"]):
|
||||
|
@ -827,9 +811,6 @@ class SPMCase(TestCase, AdaptedConfigurationTestCaseMixin):
|
|||
repo_conf_dir = self.config["spm_repos_config"] + ".d"
|
||||
os.makedirs(repo_conf_dir)
|
||||
|
||||
# Late import
|
||||
import salt.utils.files
|
||||
|
||||
with salt.utils.files.fopen(os.path.join(repo_conf_dir, "spm.repo"), "w") as fp:
|
||||
fp.write(
|
||||
textwrap.dedent(
|
||||
|
@ -1123,6 +1104,20 @@ class SSHCase(ShellCase):
|
|||
except Exception: # pylint: disable=broad-except
|
||||
return ret
|
||||
|
||||
def custom_roster(self, new_roster, data):
|
||||
'''
|
||||
helper method to create a custom roster to use for a ssh test
|
||||
'''
|
||||
roster = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'roster')
|
||||
|
||||
with salt.utils.files.fopen(roster, 'r') as fp_:
|
||||
conf = salt.utils.yaml.safe_load(fp_)
|
||||
|
||||
conf['localhost'].update(data)
|
||||
|
||||
with salt.utils.files.fopen(new_roster, 'w') as fp_:
|
||||
salt.utils.yaml.safe_dump(conf, fp_)
|
||||
|
||||
|
||||
class ClientCase(AdaptedConfigurationTestCaseMixin, TestCase):
|
||||
"""
|
||||
|
|
|
@ -18,12 +18,12 @@ import salt.utils.path
|
|||
import salt.utils.thin
|
||||
import salt.utils.yaml
|
||||
from salt.client import ssh
|
||||
from tests.support.case import ShellCase
|
||||
from tests.support.mock import MagicMock, patch
|
||||
|
||||
# Import Salt Testing libs
|
||||
from tests.support.runtests import RUNTIME_VARS
|
||||
from tests.support.unit import TestCase, skipIf
|
||||
from tests.support.case import ShellCase
|
||||
from tests.support.mock import MagicMock, patch, call
|
||||
|
||||
ROSTER = """
|
||||
localhost:
|
||||
|
@ -113,48 +113,248 @@ class SSHRosterDefaults(TestCase):
|
|||
class SSHSingleTests(TestCase):
|
||||
def setUp(self):
|
||||
self.tmp_cachedir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP)
|
||||
self.argv = ['ssh.set_auth_key', 'root', 'hobn+amNAXSBTiOXEqlBjGB...rsa root@master']
|
||||
self.opts = {
|
||||
'argv': self.argv,
|
||||
'__role': 'master',
|
||||
'cachedir': self.tmp_cachedir,
|
||||
'extension_modules': os.path.join(self.tmp_cachedir, 'extmods'),
|
||||
}
|
||||
self.target = {
|
||||
'passwd': 'abc123',
|
||||
'ssh_options': None,
|
||||
'sudo': False,
|
||||
'identities_only': False,
|
||||
'host': 'login1',
|
||||
'user': 'root',
|
||||
'timeout': 65,
|
||||
'remote_port_forwards': None,
|
||||
'sudo_user': '',
|
||||
'port': '22',
|
||||
'priv': '/etc/salt/pki/master/ssh/salt-ssh.rsa'
|
||||
}
|
||||
|
||||
def test_single_opts(self):
|
||||
""" Sanity check for ssh.Single options
|
||||
"""
|
||||
argv = ["ssh.set_auth_key", "root", "hobn+amNAXSBTiOXEqlBjGB...rsa root@master"]
|
||||
opts = {
|
||||
"argv": argv,
|
||||
"__role": "master",
|
||||
"cachedir": self.tmp_cachedir,
|
||||
"extension_modules": os.path.join(self.tmp_cachedir, "extmods"),
|
||||
}
|
||||
target = {
|
||||
"passwd": "abc123",
|
||||
"ssh_options": None,
|
||||
"sudo": False,
|
||||
"identities_only": False,
|
||||
"host": "login1",
|
||||
"user": "root",
|
||||
"timeout": 65,
|
||||
"remote_port_forwards": None,
|
||||
"sudo_user": "",
|
||||
"port": "22",
|
||||
"priv": "/etc/salt/pki/master/ssh/salt-ssh.rsa",
|
||||
}
|
||||
''' Sanity check for ssh.Single options
|
||||
'''
|
||||
|
||||
single = ssh.Single(
|
||||
opts,
|
||||
opts["argv"],
|
||||
"localhost",
|
||||
mods={},
|
||||
fsclient=None,
|
||||
thin=salt.utils.thin.thin_path(opts["cachedir"]),
|
||||
mine=False,
|
||||
**target
|
||||
)
|
||||
self.opts,
|
||||
self.opts['argv'],
|
||||
'localhost',
|
||||
mods={},
|
||||
fsclient=None,
|
||||
thin=salt.utils.thin.thin_path(self.opts['cachedir']),
|
||||
mine=False,
|
||||
**self.target)
|
||||
|
||||
self.assertEqual(single.shell._ssh_opts(), "")
|
||||
self.assertEqual(
|
||||
single.shell._cmd_str("date +%s"),
|
||||
"ssh login1 "
|
||||
"-o KbdInteractiveAuthentication=no -o "
|
||||
"PasswordAuthentication=yes -o ConnectTimeout=65 -o Port=22 "
|
||||
"-o IdentityFile=/etc/salt/pki/master/ssh/salt-ssh.rsa "
|
||||
"-o User=root date +%s",
|
||||
)
|
||||
self.assertEqual(single.shell._ssh_opts(), '')
|
||||
self.assertEqual(single.shell._cmd_str('date +%s'), 'ssh login1 '
|
||||
'-o KbdInteractiveAuthentication=no -o '
|
||||
'PasswordAuthentication=yes -o ConnectTimeout=65 -o Port=22 '
|
||||
'-o IdentityFile=/etc/salt/pki/master/ssh/salt-ssh.rsa '
|
||||
'-o User=root date +%s')
|
||||
|
||||
def test_run_with_pre_flight(self):
|
||||
'''
|
||||
test Single.run() when ssh_pre_flight is set
|
||||
and script successfully runs
|
||||
'''
|
||||
target = self.target.copy()
|
||||
target['ssh_pre_flight'] = os.path.join(RUNTIME_VARS.TMP, 'script.sh')
|
||||
single = ssh.Single(
|
||||
self.opts,
|
||||
self.opts['argv'],
|
||||
'localhost',
|
||||
mods={},
|
||||
fsclient=None,
|
||||
thin=salt.utils.thin.thin_path(self.opts['cachedir']),
|
||||
mine=False,
|
||||
**target)
|
||||
|
||||
cmd_ret = ('Success', '', 0)
|
||||
mock_flight = MagicMock(return_value=cmd_ret)
|
||||
mock_cmd = MagicMock(return_value=cmd_ret)
|
||||
patch_flight = patch('salt.client.ssh.Single.run_ssh_pre_flight', mock_flight)
|
||||
patch_cmd = patch('salt.client.ssh.Single.cmd_block', mock_cmd)
|
||||
patch_os = patch('os.path.exists', side_effect=[False, True])
|
||||
|
||||
with patch_os, patch_flight, patch_cmd:
|
||||
ret = single.run()
|
||||
mock_cmd.assert_called()
|
||||
mock_flight.assert_called()
|
||||
assert ret == cmd_ret
|
||||
|
||||
def test_run_with_pre_flight_stderr(self):
|
||||
'''
|
||||
test Single.run() when ssh_pre_flight is set
|
||||
and script errors when run
|
||||
'''
|
||||
target = self.target.copy()
|
||||
target['ssh_pre_flight'] = os.path.join(RUNTIME_VARS.TMP, 'script.sh')
|
||||
single = ssh.Single(
|
||||
self.opts,
|
||||
self.opts['argv'],
|
||||
'localhost',
|
||||
mods={},
|
||||
fsclient=None,
|
||||
thin=salt.utils.thin.thin_path(self.opts['cachedir']),
|
||||
mine=False,
|
||||
**target)
|
||||
|
||||
cmd_ret = ('', 'Error running script', 1)
|
||||
mock_flight = MagicMock(return_value=cmd_ret)
|
||||
mock_cmd = MagicMock(return_value=cmd_ret)
|
||||
patch_flight = patch('salt.client.ssh.Single.run_ssh_pre_flight', mock_flight)
|
||||
patch_cmd = patch('salt.client.ssh.Single.cmd_block', mock_cmd)
|
||||
patch_os = patch('os.path.exists', side_effect=[False, True])
|
||||
|
||||
with patch_os, patch_flight, patch_cmd:
|
||||
ret = single.run()
|
||||
mock_cmd.assert_not_called()
|
||||
mock_flight.assert_called()
|
||||
assert ret == cmd_ret
|
||||
|
||||
def test_run_with_pre_flight_script_doesnot_exist(self):
|
||||
'''
|
||||
test Single.run() when ssh_pre_flight is set
|
||||
and the script does not exist
|
||||
'''
|
||||
target = self.target.copy()
|
||||
target['ssh_pre_flight'] = os.path.join(RUNTIME_VARS.TMP, 'script.sh')
|
||||
single = ssh.Single(
|
||||
self.opts,
|
||||
self.opts['argv'],
|
||||
'localhost',
|
||||
mods={},
|
||||
fsclient=None,
|
||||
thin=salt.utils.thin.thin_path(self.opts['cachedir']),
|
||||
mine=False,
|
||||
**target)
|
||||
|
||||
cmd_ret = ('Success', '', 0)
|
||||
mock_flight = MagicMock(return_value=cmd_ret)
|
||||
mock_cmd = MagicMock(return_value=cmd_ret)
|
||||
patch_flight = patch('salt.client.ssh.Single.run_ssh_pre_flight', mock_flight)
|
||||
patch_cmd = patch('salt.client.ssh.Single.cmd_block', mock_cmd)
|
||||
patch_os = patch('os.path.exists', side_effect=[False, False])
|
||||
|
||||
with patch_os, patch_flight, patch_cmd:
|
||||
ret = single.run()
|
||||
mock_cmd.assert_called()
|
||||
mock_flight.assert_not_called()
|
||||
assert ret == cmd_ret
|
||||
|
||||
def test_run_with_pre_flight_thin_dir_exists(self):
|
||||
'''
|
||||
test Single.run() when ssh_pre_flight is set
|
||||
and thin_dir already exists
|
||||
'''
|
||||
target = self.target.copy()
|
||||
target['ssh_pre_flight'] = os.path.join(RUNTIME_VARS.TMP, 'script.sh')
|
||||
single = ssh.Single(
|
||||
self.opts,
|
||||
self.opts['argv'],
|
||||
'localhost',
|
||||
mods={},
|
||||
fsclient=None,
|
||||
thin=salt.utils.thin.thin_path(self.opts['cachedir']),
|
||||
mine=False,
|
||||
**target)
|
||||
|
||||
cmd_ret = ('', 'Error running script', 1)
|
||||
mock_flight = MagicMock(return_value=cmd_ret)
|
||||
mock_cmd = MagicMock(return_value=cmd_ret)
|
||||
patch_flight = patch('salt.client.ssh.Single.run_ssh_pre_flight', mock_flight)
|
||||
patch_cmd = patch('salt.client.ssh.Single.cmd_block', mock_cmd)
|
||||
patch_os = patch('os.path.exists', return_value=True)
|
||||
|
||||
with patch_os, patch_flight, patch_cmd:
|
||||
ret = single.run()
|
||||
mock_cmd.assert_called()
|
||||
mock_flight.assert_not_called()
|
||||
assert ret == cmd_ret
|
||||
|
||||
def test_execute_script(self):
|
||||
'''
|
||||
test Single.execute_script()
|
||||
'''
|
||||
single = ssh.Single(
|
||||
self.opts,
|
||||
self.opts['argv'],
|
||||
'localhost',
|
||||
mods={},
|
||||
fsclient=None,
|
||||
thin=salt.utils.thin.thin_path(self.opts['cachedir']),
|
||||
mine=False,
|
||||
winrm=False,
|
||||
**self.target)
|
||||
|
||||
exp_ret = ('Success', '', 0)
|
||||
mock_cmd = MagicMock(return_value=exp_ret)
|
||||
patch_cmd = patch('salt.client.ssh.shell.Shell.exec_cmd', mock_cmd)
|
||||
script = os.path.join(RUNTIME_VARS.TMP, 'script.sh')
|
||||
|
||||
with patch_cmd:
|
||||
ret = single.execute_script(script=script)
|
||||
assert ret == exp_ret
|
||||
assert mock_cmd.call_count == 2
|
||||
assert [call("/bin/sh '{0}'".format(script)),
|
||||
call("rm '{0}'".format(script))] == mock_cmd.call_args_list
|
||||
|
||||
def test_shim_cmd(self):
|
||||
'''
|
||||
test Single.shim_cmd()
|
||||
'''
|
||||
single = ssh.Single(
|
||||
self.opts,
|
||||
self.opts['argv'],
|
||||
'localhost',
|
||||
mods={},
|
||||
fsclient=None,
|
||||
thin=salt.utils.thin.thin_path(self.opts['cachedir']),
|
||||
mine=False,
|
||||
winrm=False,
|
||||
tty=True,
|
||||
**self.target)
|
||||
|
||||
exp_ret = ('Success', '', 0)
|
||||
mock_cmd = MagicMock(return_value=exp_ret)
|
||||
patch_cmd = patch('salt.client.ssh.shell.Shell.exec_cmd', mock_cmd)
|
||||
patch_rand = patch('os.urandom', return_value=b'5\xd9l\xca\xc2\xff')
|
||||
|
||||
with patch_cmd, patch_rand:
|
||||
ret = single.shim_cmd(cmd_str='echo test')
|
||||
assert ret == exp_ret
|
||||
assert [call('mkdir -p '),
|
||||
call("/bin/sh '$HOME/.35d96ccac2ff.py'"),
|
||||
call("rm '$HOME/.35d96ccac2ff.py'")] == mock_cmd.call_args_list
|
||||
|
||||
def test_run_ssh_pre_flight(self):
|
||||
'''
|
||||
test Single.run_ssh_pre_flight
|
||||
'''
|
||||
target = self.target.copy()
|
||||
target['ssh_pre_flight'] = os.path.join(RUNTIME_VARS.TMP, 'script.sh')
|
||||
single = ssh.Single(
|
||||
self.opts,
|
||||
self.opts['argv'],
|
||||
'localhost',
|
||||
mods={},
|
||||
fsclient=None,
|
||||
thin=salt.utils.thin.thin_path(self.opts['cachedir']),
|
||||
mine=False,
|
||||
winrm=False,
|
||||
tty=True,
|
||||
**target)
|
||||
|
||||
exp_ret = ('Success', '', 0)
|
||||
mock_cmd = MagicMock(return_value=exp_ret)
|
||||
patch_cmd = patch('salt.client.ssh.shell.Shell.exec_cmd', mock_cmd)
|
||||
exp_tmp = os.path.join(tempfile.gettempdir(), os.path.basename(target['ssh_pre_flight']))
|
||||
|
||||
with patch_cmd:
|
||||
ret = single.run_ssh_pre_flight()
|
||||
assert ret == exp_ret
|
||||
assert [call("/bin/sh '{0}'".format(exp_tmp)),
|
||||
call("rm '{0}'".format(exp_tmp))] == mock_cmd.call_args_list
|
||||
|
|
Loading…
Add table
Reference in a new issue