Merge pull request #47196 from twangboy/fix_47024

Fix issues with pip
This commit is contained in:
Nicole Thomas 2018-05-04 10:23:03 -04:00 committed by GitHub
commit da9871d36b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 837 additions and 653 deletions

View File

@ -77,6 +77,7 @@ of the 2015.5 branch:
from __future__ import absolute_import from __future__ import absolute_import
# Import python libs # Import python libs
import json
import os import os
import re import re
import shutil import shutil
@ -114,43 +115,82 @@ def __virtual__():
return 'pip' return 'pip'
def _clear_context(bin_env=None):
'''
Remove the cached pip version
'''
contextkey = 'pip.version'
if bin_env is not None:
contextkey = '{0}.{1}'.format(contextkey, bin_env)
__context__.pop(contextkey, None)
def _get_pip_bin(bin_env): def _get_pip_bin(bin_env):
''' '''
Locate the pip binary, either from `bin_env` as a virtualenv, as the Locate the pip binary, either from `bin_env` as a virtualenv, as the
executable itself, or from searching conventional filesystem locations executable itself, or from searching conventional filesystem locations
''' '''
if not bin_env: if not bin_env:
which_result = __salt__['cmd.which_bin']( logger.debug('pip: Using pip from currently-running Python')
['pip{0}.{1}'.format(*sys.version_info[:2]), return [os.path.normpath(sys.executable), '-m', 'pip']
'pip{0}'.format(sys.version_info[0]),
'pip', 'pip-python'] # try to get python bin from virtualenv, bin_env
) python_bin = 'python.exe' if salt.utils.is_windows() else 'python'
if salt.utils.is_windows() and six.PY2:
which_result.encode('string-escape') def _search_paths(*basedirs):
if which_result is None: ret = []
raise CommandNotFoundError('Could not find a `pip` binary') for path in basedirs:
return which_result ret.extend([
os.path.join(path, python_bin),
os.path.join(path, 'bin', python_bin),
os.path.join(path, 'Scripts', python_bin)
])
return ret
# try to get pip bin from virtualenv, bin_env
if os.path.isdir(bin_env): if os.path.isdir(bin_env):
if salt.utils.is_windows(): for bin_path in _search_paths(bin_env):
if six.PY2: if os.path.isfile(bin_path):
pip_bin = os.path.join( if os.access(bin_path, os.X_OK):
bin_env, 'Scripts', 'pip.exe').encode('string-escape') logger.debug('pip: Found python binary: %s', bin_path)
else: return [os.path.normpath(bin_path), '-m', 'pip']
pip_bin = os.path.join(bin_env, 'Scripts', 'pip.exe') else:
else: logger.debug(
pip_bin = os.path.join(bin_env, 'bin', 'pip') 'pip: Found python binary by name but it is not '
if os.path.isfile(pip_bin): 'executable: %s', bin_path
return pip_bin )
msg = 'Could not find a `pip` binary in virtualenv {0}'.format(bin_env) raise CommandNotFoundError(
raise CommandNotFoundError(msg) 'Could not find a pip binary in virtualenv {0}'.format(bin_env)
# bin_env is the pip binary )
# bin_env is the python or pip binary
elif os.access(bin_env, os.X_OK): elif os.access(bin_env, os.X_OK):
if os.path.isfile(bin_env) or os.path.islink(bin_env): if os.path.isfile(bin_env):
return bin_env # If the python binary was passed, return it
if 'python' in os.path.basename(bin_env):
return [os.path.normpath(bin_env), '-m', 'pip']
# Try to find the python binary based on the location of pip in a
# virtual environment, should be relative
if 'pip' in os.path.basename(bin_env):
# Look in the same directory as the pip binary, and also its
# parent directories.
pip_dirname = os.path.dirname(bin_env)
pip_parent_dir = os.path.dirname(pip_dirname)
for bin_path in _search_paths(pip_dirname, pip_parent_dir):
if os.path.isfile(bin_path):
logger.debug('pip: Found python binary: %s', bin_path)
return [os.path.normpath(bin_path), '-m', 'pip']
# Couldn't find python, use the passed pip binary
# This has the limitation of being unable to update pip itself
return [os.path.normpath(bin_env)]
raise CommandExecutionError(
'Could not find a pip binary within {0}'.format(bin_env)
)
else: else:
raise CommandNotFoundError('Could not find a `pip` binary') raise CommandNotFoundError(
'Access denied to {0}, could not find a pip binary'.format(bin_env)
)
def _get_cached_requirements(requirements, saltenv): def _get_cached_requirements(requirements, saltenv):
@ -267,15 +307,21 @@ def _process_requirements(requirements, cmd, cwd, saltenv, user):
treq = tempfile.mkdtemp() treq = tempfile.mkdtemp()
__salt__['file.chown'](treq, user, None) __salt__['file.chown'](treq, user, None)
# In Windows, just being owner of a file isn't enough. You also
# need permissions
if salt.utils.is_windows():
__utils__['win_dacl.set_permissions'](
obj_name=treq,
principal=user,
permissions='read_execute')
current_directory = None current_directory = None
if not current_directory: if not current_directory:
current_directory = os.path.abspath(os.curdir) current_directory = os.path.abspath(os.curdir)
logger.info('_process_requirements from directory,' + logger.info('_process_requirements from directory, '
'%s -- requirement: %s', cwd, requirement '%s -- requirement: %s', cwd, requirement)
)
if cwd is None: if cwd is None:
r = requirement r = requirement
@ -378,7 +424,6 @@ def install(pkgs=None, # pylint: disable=R0912,R0913,R0914
global_options=None, global_options=None,
install_options=None, install_options=None,
user=None, user=None,
no_chown=False,
cwd=None, cwd=None,
pre_releases=False, pre_releases=False,
cert=None, cert=None,
@ -392,7 +437,8 @@ def install(pkgs=None, # pylint: disable=R0912,R0913,R0914
trusted_host=None, trusted_host=None,
no_cache_dir=False, no_cache_dir=False,
cache_dir=None, cache_dir=None,
no_binary=None): no_binary=None,
**kwargs):
''' '''
Install packages with pip Install packages with pip
@ -514,10 +560,6 @@ def install(pkgs=None, # pylint: disable=R0912,R0913,R0914
user user
The user under which to run pip The user under which to run pip
no_chown
When user is given, do not attempt to copy and chown a requirements
file
cwd cwd
Current working directory to run pip from Current working directory to run pip from
@ -578,9 +620,14 @@ def install(pkgs=None, # pylint: disable=R0912,R0913,R0914
editable=git+https://github.com/worldcompany/djangoembed.git#egg=djangoembed upgrade=True no_deps=True editable=git+https://github.com/worldcompany/djangoembed.git#egg=djangoembed upgrade=True no_deps=True
''' '''
pip_bin = _get_pip_bin(bin_env) if 'no_chown' in kwargs:
salt.utils.warn_until(
cmd = [pip_bin, 'install'] 'Flourine',
'The no_chown argument has been deprecated and is no longer used. '
'Its functionality was removed in Boron.')
kwargs.pop('no_chown')
cmd = _get_pip_bin(bin_env)
cmd.append('install')
cleanup_requirements, error = _process_requirements( cleanup_requirements, error = _process_requirements(
requirements=requirements, requirements=requirements,
@ -593,10 +640,11 @@ def install(pkgs=None, # pylint: disable=R0912,R0913,R0914
if error: if error:
return error return error
cur_version = version(bin_env)
if use_wheel: if use_wheel:
min_version = '1.4' min_version = '1.4'
max_version = '9.0.3' max_version = '9.0.3'
cur_version = __salt__['pip.version'](bin_env)
too_low = salt.utils.compare_versions(ver1=cur_version, oper='<', ver2=min_version) too_low = salt.utils.compare_versions(ver1=cur_version, oper='<', ver2=min_version)
too_high = salt.utils.compare_versions(ver1=cur_version, oper='>', ver2=max_version) too_high = salt.utils.compare_versions(ver1=cur_version, oper='>', ver2=max_version)
if too_low or too_high: if too_low or too_high:
@ -611,7 +659,6 @@ def install(pkgs=None, # pylint: disable=R0912,R0913,R0914
if no_use_wheel: if no_use_wheel:
min_version = '1.4' min_version = '1.4'
max_version = '9.0.3' max_version = '9.0.3'
cur_version = __salt__['pip.version'](bin_env)
too_low = salt.utils.compare_versions(ver1=cur_version, oper='<', ver2=min_version) too_low = salt.utils.compare_versions(ver1=cur_version, oper='<', ver2=min_version)
too_high = salt.utils.compare_versions(ver1=cur_version, oper='>', ver2=max_version) too_high = salt.utils.compare_versions(ver1=cur_version, oper='>', ver2=max_version)
if too_low or too_high: if too_low or too_high:
@ -625,7 +672,6 @@ def install(pkgs=None, # pylint: disable=R0912,R0913,R0914
if no_binary: if no_binary:
min_version = '7.0.0' min_version = '7.0.0'
cur_version = __salt__['pip.version'](bin_env)
too_low = salt.utils.compare_versions(ver1=cur_version, oper='<', ver2=min_version) too_low = salt.utils.compare_versions(ver1=cur_version, oper='<', ver2=min_version)
if too_low: if too_low:
logger.error( logger.error(
@ -700,8 +746,7 @@ def install(pkgs=None, # pylint: disable=R0912,R0913,R0914
if mirrors: if mirrors:
# https://github.com/pypa/pip/pull/2641/files#diff-3ef137fb9ffdd400f117a565cd94c188L216 # https://github.com/pypa/pip/pull/2641/files#diff-3ef137fb9ffdd400f117a565cd94c188L216
pip_version = version(pip_bin) if salt.utils.compare_versions(ver1=cur_version, oper='>=', ver2='7.0.0'):
if salt.utils.compare_versions(ver1=pip_version, oper='>=', ver2='7.0.0'):
raise CommandExecutionError( raise CommandExecutionError(
'pip >= 7.0.0 does not support mirror argument:' 'pip >= 7.0.0 does not support mirror argument:'
' use index_url and/or extra_index_url instead' ' use index_url and/or extra_index_url instead'
@ -729,7 +774,7 @@ def install(pkgs=None, # pylint: disable=R0912,R0913,R0914
if download_cache or cache_dir: if download_cache or cache_dir:
cmd.extend(['--cache-dir' if salt.utils.compare_versions( cmd.extend(['--cache-dir' if salt.utils.compare_versions(
ver1=version(bin_env), oper='>=', ver2='6.0' ver1=cur_version, oper='>=', ver2='6.0'
) else '--download-cache', download_cache or cache_dir]) ) else '--download-cache', download_cache or cache_dir])
if source: if source:
@ -766,7 +811,7 @@ def install(pkgs=None, # pylint: disable=R0912,R0913,R0914
if pre_releases: if pre_releases:
# Check the locally installed pip version # Check the locally installed pip version
pip_version = version(pip_bin) pip_version = cur_version
# From pip v1.4 the --pre flag is available # From pip v1.4 the --pre flag is available
if salt.utils.compare_versions(ver1=pip_version, oper='>=', ver2='1.4'): if salt.utils.compare_versions(ver1=pip_version, oper='>=', ver2='1.4'):
@ -847,6 +892,9 @@ def install(pkgs=None, # pylint: disable=R0912,R0913,R0914
cmd_kwargs = dict(saltenv=saltenv, use_vt=use_vt, runas=user) cmd_kwargs = dict(saltenv=saltenv, use_vt=use_vt, runas=user)
if kwargs:
cmd_kwargs.update(kwargs)
if env_vars: if env_vars:
cmd_kwargs.setdefault('env', {}).update(_format_env_vars(env_vars)) cmd_kwargs.setdefault('env', {}).update(_format_env_vars(env_vars))
@ -866,6 +914,7 @@ def install(pkgs=None, # pylint: disable=R0912,R0913,R0914
python_shell=False, python_shell=False,
**cmd_kwargs) **cmd_kwargs)
finally: finally:
_clear_context(bin_env)
for tempdir in [cr for cr in cleanup_requirements if cr is not None]: for tempdir in [cr for cr in cleanup_requirements if cr is not None]:
if os.path.isdir(tempdir): if os.path.isdir(tempdir):
shutil.rmtree(tempdir) shutil.rmtree(tempdir)
@ -878,7 +927,6 @@ def uninstall(pkgs=None,
proxy=None, proxy=None,
timeout=None, timeout=None,
user=None, user=None,
no_chown=False,
cwd=None, cwd=None,
saltenv='base', saltenv='base',
use_vt=False): use_vt=False):
@ -911,11 +959,6 @@ def uninstall(pkgs=None,
Set the socket timeout (default 15 seconds) Set the socket timeout (default 15 seconds)
user user
The user under which to run pip The user under which to run pip
no_chown
When user is given, do not attempt to copy and chown
a requirements file (needed if the requirements file refers to other
files via relative paths, as the copy-and-chown procedure does not
account for such files)
cwd cwd
Current working directory to run pip from Current working directory to run pip from
use_vt use_vt
@ -931,9 +974,8 @@ def uninstall(pkgs=None,
salt '*' pip.uninstall <package name> bin_env=/path/to/pip_bin salt '*' pip.uninstall <package name> bin_env=/path/to/pip_bin
''' '''
pip_bin = _get_pip_bin(bin_env) cmd = _get_pip_bin(bin_env)
cmd.extend(['uninstall', '-y'])
cmd = [pip_bin, 'uninstall', '-y']
cleanup_requirements, error = _process_requirements( cleanup_requirements, error = _process_requirements(
requirements=requirements, cmd=cmd, saltenv=saltenv, user=user, requirements=requirements, cmd=cmd, saltenv=saltenv, user=user,
@ -992,6 +1034,7 @@ def uninstall(pkgs=None,
try: try:
return __salt__['cmd.run_all'](cmd, **cmd_kwargs) return __salt__['cmd.run_all'](cmd, **cmd_kwargs)
finally: finally:
_clear_context(bin_env)
for requirement in cleanup_requirements: for requirement in cleanup_requirements:
if requirement: if requirement:
try: try:
@ -1004,7 +1047,8 @@ def freeze(bin_env=None,
user=None, user=None,
cwd=None, cwd=None,
use_vt=False, use_vt=False,
env_vars=None): env_vars=None,
**kwargs):
''' '''
Return a list of installed packages either globally or in the specified Return a list of installed packages either globally or in the specified
virtualenv virtualenv
@ -1037,15 +1081,13 @@ def freeze(bin_env=None,
The packages pip, wheel, setuptools, and distribute are included if the The packages pip, wheel, setuptools, and distribute are included if the
installed pip is new enough. installed pip is new enough.
''' '''
pip_bin = _get_pip_bin(bin_env) cmd = _get_pip_bin(bin_env)
cmd.append('freeze')
cmd = [pip_bin, 'freeze']
# Include pip, setuptools, distribute, wheel # Include pip, setuptools, distribute, wheel
min_version = '8.0.3' min_version = '8.0.3'
cur_version = version(bin_env) cur_version = version(bin_env)
if not salt.utils.compare_versions(ver1=cur_version, oper='>=', if salt.utils.compare_versions(ver1=cur_version, oper='<', ver2=min_version):
ver2=min_version):
logger.warning( logger.warning(
('The version of pip installed is {0}, which is older than {1}. ' ('The version of pip installed is {0}, which is older than {1}. '
'The packages pip, wheel, setuptools, and distribute will not be ' 'The packages pip, wheel, setuptools, and distribute will not be '
@ -1055,14 +1097,16 @@ def freeze(bin_env=None,
cmd.append('--all') cmd.append('--all')
cmd_kwargs = dict(runas=user, cwd=cwd, use_vt=use_vt, python_shell=False) cmd_kwargs = dict(runas=user, cwd=cwd, use_vt=use_vt, python_shell=False)
if kwargs:
cmd_kwargs.update(**kwargs)
if bin_env and os.path.isdir(bin_env): if bin_env and os.path.isdir(bin_env):
cmd_kwargs['env'] = {'VIRTUAL_ENV': bin_env} cmd_kwargs['env'] = {'VIRTUAL_ENV': bin_env}
if env_vars: if env_vars:
cmd_kwargs.setdefault('env', {}).update(_format_env_vars(env_vars)) cmd_kwargs.setdefault('env', {}).update(_format_env_vars(env_vars))
result = __salt__['cmd.run_all'](cmd, **cmd_kwargs) result = __salt__['cmd.run_all'](cmd, **cmd_kwargs)
if result['retcode'] > 0: if result['retcode']:
raise CommandExecutionError(result['stderr']) raise CommandExecutionError(result['stderr'], info=result)
return result['stdout'].splitlines() return result['stdout'].splitlines()
@ -1071,7 +1115,8 @@ def list_(prefix=None,
bin_env=None, bin_env=None,
user=None, user=None,
cwd=None, cwd=None,
env_vars=None): env_vars=None,
**kwargs):
''' '''
Filter list of installed apps from ``freeze`` and check to see if Filter list of installed apps from ``freeze`` and check to see if
``prefix`` exists in the list of packages installed. ``prefix`` exists in the list of packages installed.
@ -1100,7 +1145,11 @@ def list_(prefix=None,
if prefix is None or 'pip'.startswith(prefix): if prefix is None or 'pip'.startswith(prefix):
packages['pip'] = version(bin_env) packages['pip'] = version(bin_env)
for line in freeze(bin_env=bin_env, user=user, cwd=cwd, env_vars=env_vars): for line in freeze(bin_env=bin_env,
user=user,
cwd=cwd,
env_vars=env_vars,
**kwargs):
if line.startswith('-f') or line.startswith('#'): if line.startswith('-f') or line.startswith('#'):
# ignore -f line as it contains --find-links directory # ignore -f line as it contains --find-links directory
# ignore comment lines # ignore comment lines
@ -1110,7 +1159,15 @@ def list_(prefix=None,
continue continue
elif line.startswith('-e'): elif line.startswith('-e'):
line = line.split('-e ')[1] line = line.split('-e ')[1]
version_, name = line.split('#egg=') if '#egg=' in line:
version_, name = line.split('#egg=')
else:
if len(line.split('===')) >= 2:
name = line.split('===')[0]
version_ = line.split('===')[1]
elif len(line.split('==')) >= 2:
name = line.split('==')[0]
version_ = line.split('==')[1]
elif len(line.split('===')) >= 2: elif len(line.split('===')) >= 2:
name = line.split('===')[0] name = line.split('===')[0]
version_ = line.split('===')[1] version_ = line.split('===')[1]
@ -1145,14 +1202,27 @@ def version(bin_env=None):
salt '*' pip.version salt '*' pip.version
''' '''
pip_bin = _get_pip_bin(bin_env) contextkey = 'pip.version'
if bin_env is not None:
contextkey = '{0}.{1}'.format(contextkey, bin_env)
if contextkey in __context__:
return __context__[contextkey]
cmd = _get_pip_bin(bin_env)[:]
cmd.append('--version')
ret = __salt__['cmd.run_all'](cmd, python_shell=False)
if ret['retcode']:
raise CommandNotFoundError('Could not find a `pip` binary')
output = __salt__['cmd.run'](
'{0} --version'.format(pip_bin), python_shell=False)
try: try:
return re.match(r'^pip (\S+)', output).group(1) pip_version = re.match(r'^pip (\S+)', ret['stdout']).group(1)
except AttributeError: except AttributeError:
return None pip_version = None
__context__[contextkey] = pip_version
return pip_version
def list_upgrades(bin_env=None, def list_upgrades(bin_env=None,
@ -1167,28 +1237,66 @@ def list_upgrades(bin_env=None,
salt '*' pip.list_upgrades salt '*' pip.list_upgrades
''' '''
pip_bin = _get_pip_bin(bin_env) cmd = _get_pip_bin(bin_env)
cmd.extend(['list', '--outdated'])
cmd = [pip_bin, 'list', '--outdated'] pip_version = version(bin_env)
# Pip started supporting the ability to output json starting with 9.0.0
if salt.utils.compare_versions(ver1=pip_version, oper='>=', ver2='9.0.0'):
cmd.extend(['--format', 'json'])
cmd_kwargs = dict(cwd=cwd, runas=user) cmd_kwargs = dict(cwd=cwd, runas=user)
if bin_env and os.path.isdir(bin_env): if bin_env and os.path.isdir(bin_env):
cmd_kwargs['env'] = {'VIRTUAL_ENV': bin_env} cmd_kwargs['env'] = {'VIRTUAL_ENV': bin_env}
result = __salt__['cmd.run_all'](cmd, **cmd_kwargs) result = __salt__['cmd.run_all'](cmd, **cmd_kwargs)
if result['retcode'] > 0: if result['retcode']:
logger.error(result['stderr']) raise CommandExecutionError(result['stderr'], info=result)
raise CommandExecutionError(result['stderr'])
packages = {} packages = {}
for line in result['stdout'].splitlines(): # Pip started supporting the ability to output json starting with 9.0.0
match = re.search(r'(\S*)\s+\(.*Latest:\s+(.*)\)', line) # Older versions will have to parse stdout
if match: if salt.utils.compare_versions(ver1=pip_version, oper='<', ver2='9.0.0'):
name, version_ = match.groups() # Pip versions < 8.0.0 had a different output format
# Sample data:
# pip (Current: 7.1.2 Latest: 10.0.1 [wheel])
# psutil (Current: 5.2.2 Latest: 5.4.5 [wheel])
# pyasn1 (Current: 0.2.3 Latest: 0.4.2 [wheel])
# pycparser (Current: 2.17 Latest: 2.18 [sdist])
if salt.utils.compare_versions(ver1=pip_version, oper='<', ver2='8.0.0'):
logger.debug('pip module: Old output format')
pat = re.compile(r'(\S*)\s+\(.*Latest:\s+(.*)\)')
# New output format for version 8.0.0+
# Sample data:
# pip (8.0.0) - Latest: 10.0.1 [wheel]
# psutil (5.2.2) - Latest: 5.4.5 [wheel]
# pyasn1 (0.2.3) - Latest: 0.4.2 [wheel]
# pycparser (2.17) - Latest: 2.18 [sdist]
else: else:
logger.error('Can\'t parse line \'{0}\''.format(line)) logger.debug('pip module: New output format')
continue pat = re.compile(r'(\S*)\s+\(.*\)\s+-\s+Latest:\s+(.*)')
packages[name] = version_
for line in result['stdout'].splitlines():
match = pat.search(line)
if match:
name, version_ = match.groups()
else:
logger.error('Can\'t parse line \'{0}\''.format(line))
continue
packages[name] = version_
else:
logger.debug('pip module: JSON output format')
try:
pkgs = json.loads(result['stdout'], strict=False)
except ValueError:
raise CommandExecutionError('Invalid JSON', info=result)
for pkg in pkgs:
packages[pkg['name']] = '{0} [{1}]'.format(pkg['latest_version'],
pkg['latest_filetype'])
return packages return packages
@ -1217,7 +1325,11 @@ def upgrade(bin_env=None,
''' '''
.. versionadded:: 2015.5.0 .. versionadded:: 2015.5.0
Upgrades outdated pip packages Upgrades outdated pip packages.
.. note::
On Windows you can't update salt from pip using salt, so salt will be
skipped
Returns a dict containing the changes. Returns a dict containing the changes.
@ -1235,16 +1347,19 @@ def upgrade(bin_env=None,
'result': True, 'result': True,
'comment': '', 'comment': '',
} }
pip_bin = _get_pip_bin(bin_env) cmd = _get_pip_bin(bin_env)
cmd.extend(['install', '-U'])
old = list_(bin_env=bin_env, user=user, cwd=cwd) old = list_(bin_env=bin_env, user=user, cwd=cwd)
cmd = [pip_bin, 'install', '-U']
cmd_kwargs = dict(cwd=cwd, use_vt=use_vt) cmd_kwargs = dict(cwd=cwd, use_vt=use_vt)
if bin_env and os.path.isdir(bin_env): if bin_env and os.path.isdir(bin_env):
cmd_kwargs['env'] = {'VIRTUAL_ENV': bin_env} cmd_kwargs['env'] = {'VIRTUAL_ENV': bin_env}
errors = False errors = False
for pkg in list_upgrades(bin_env=bin_env, user=user, cwd=cwd): for pkg in list_upgrades(bin_env=bin_env, user=user, cwd=cwd):
if pkg == 'salt':
if salt.utils.is_windows():
continue
result = __salt__['cmd.run_all'](cmd + [pkg], **cmd_kwargs) result = __salt__['cmd.run_all'](cmd + [pkg], **cmd_kwargs)
if result['retcode'] != 0: if result['retcode'] != 0:
errors = True errors = True
@ -1253,6 +1368,7 @@ def upgrade(bin_env=None,
if errors: if errors:
ret['result'] = False ret['result'] = False
_clear_context(bin_env)
new = list_(bin_env=bin_env, user=user, cwd=cwd) new = list_(bin_env=bin_env, user=user, cwd=cwd)
ret['changes'] = salt.utils.compare_dicts(old, new) ret['changes'] = salt.utils.compare_dicts(old, new)
@ -1301,9 +1417,8 @@ def list_all_versions(pkg,
salt '*' pip.list_all_versions <package name> salt '*' pip.list_all_versions <package name>
''' '''
pip_bin = _get_pip_bin(bin_env) cmd = _get_pip_bin(bin_env)
cmd.extend(['install', '{0}==versions'.format(pkg)])
cmd = [pip_bin, 'install', '{0}==versions'.format(pkg)]
cmd_kwargs = dict(cwd=cwd, runas=user, output_loglevel='quiet', redirect_stderr=True) cmd_kwargs = dict(cwd=cwd, runas=user, output_loglevel='quiet', redirect_stderr=True)
if bin_env and os.path.isdir(bin_env): if bin_env and os.path.isdir(bin_env):

View File

@ -56,7 +56,8 @@ def create(path,
upgrade=None, upgrade=None,
user=None, user=None,
use_vt=False, use_vt=False,
saltenv='base'): saltenv='base',
**kwargs):
''' '''
Create a virtualenv Create a virtualenv
@ -102,6 +103,11 @@ def create(path,
user : None user : None
Set ownership for the virtualenv Set ownership for the virtualenv
.. note::
On Windows you must also pass a ``password`` parameter. Additionally,
the user must have permissions to the location where the virtual
environment is being created
runas : None runas : None
Set ownership for the virtualenv Set ownership for the virtualenv
@ -161,7 +167,7 @@ def create(path,
# Unable to import?? Let's parse the version from the console # Unable to import?? Let's parse the version from the console
version_cmd = [venv_bin, '--version'] version_cmd = [venv_bin, '--version']
ret = __salt__['cmd.run_all']( ret = __salt__['cmd.run_all'](
version_cmd, runas=user, python_shell=False version_cmd, runas=user, python_shell=False, **kwargs
) )
if ret['retcode'] > 0 or not ret['stdout'].strip(): if ret['retcode'] > 0 or not ret['stdout'].strip():
raise CommandExecutionError( raise CommandExecutionError(
@ -251,7 +257,7 @@ def create(path,
cmd.append(path) cmd.append(path)
# Let's create the virtualenv # Let's create the virtualenv
ret = __salt__['cmd.run_all'](cmd, runas=user, python_shell=False) ret = __salt__['cmd.run_all'](cmd, runas=user, python_shell=False, **kwargs)
if ret['retcode'] != 0: if ret['retcode'] != 0:
# Something went wrong. Let's bail out now! # Something went wrong. Let's bail out now!
return ret return ret

View File

@ -184,24 +184,19 @@ def _check_pkg_version_format(pkg):
return ret return ret
def _check_if_installed(prefix, state_pkg_name, version_spec, def _check_if_installed(prefix, state_pkg_name, version_spec, ignore_installed,
ignore_installed, force_reinstall, force_reinstall, upgrade, user, cwd, bin_env, env_vars,
upgrade, user, cwd, bin_env, env_vars): **kwargs):
# result: None means the command failed to run # result: None means the command failed to run
# result: True means the package is installed # result: True means the package is installed
# result: False means the package is not installed # result: False means the package is not installed
ret = {'result': False, 'comment': None} ret = {'result': False, 'comment': None}
# Check if the requested package is already installed. # Check if the requested package is already installed.
try: pip_list = __salt__['pip.list'](prefix, bin_env=bin_env,
pip_list = __salt__['pip.list'](prefix, bin_env=bin_env, user=user, cwd=cwd,
user=user, cwd=cwd, env_vars=env_vars, **kwargs)
env_vars=env_vars) prefix_realname = _find_key(prefix, pip_list)
prefix_realname = _find_key(prefix, pip_list)
except (CommandNotFoundError, CommandExecutionError) as err:
ret['result'] = None
ret['comment'] = 'Error installing \'{0}\': {1}'.format(state_pkg_name, err)
return ret
# If the package was already installed, check # If the package was already installed, check
# the ignore_installed and force_reinstall flags # the ignore_installed and force_reinstall flags
@ -313,7 +308,6 @@ def installed(name,
install_options=None, install_options=None,
global_options=None, global_options=None,
user=None, user=None,
no_chown=False,
cwd=None, cwd=None,
pre_releases=False, pre_releases=False,
cert=None, cert=None,
@ -326,7 +320,8 @@ def installed(name,
trusted_host=None, trusted_host=None,
no_cache_dir=False, no_cache_dir=False,
cache_dir=None, cache_dir=None,
no_binary=None): no_binary=None,
**kwargs):
''' '''
Make sure the package is installed Make sure the package is installed
@ -448,10 +443,6 @@ def installed(name,
no_install no_install
Download and unpack all packages, but don't actually install them Download and unpack all packages, but don't actually install them
no_chown
When user is given, do not attempt to copy and chown
a requirements file
no_cache_dir: no_cache_dir:
Disable the cache. Disable the cache.
@ -594,6 +585,12 @@ def installed(name,
.. _`virtualenv`: http://www.virtualenv.org/en/latest/ .. _`virtualenv`: http://www.virtualenv.org/en/latest/
''' '''
if 'no_chown' in kwargs:
salt.utils.warn_until(
'Flourine',
'The no_chown argument has been deprecated and is no longer used. '
'Its functionality was removed in Boron.')
kwargs.pop('no_chown')
if pip_bin and not bin_env: if pip_bin and not bin_env:
bin_env = pip_bin bin_env = pip_bin
@ -619,11 +616,16 @@ def installed(name,
ret = {'name': ';'.join(pkgs), 'result': None, ret = {'name': ';'.join(pkgs), 'result': None,
'comment': '', 'changes': {}} 'comment': '', 'changes': {}}
try:
cur_version = __salt__['pip.version'](bin_env)
except (CommandNotFoundError, CommandExecutionError) as err:
ret['result'] = None
ret['comment'] = 'Error installing \'{0}\': {1}'.format(name, err)
return ret
# Check that the pip binary supports the 'use_wheel' option # Check that the pip binary supports the 'use_wheel' option
if use_wheel: if use_wheel:
min_version = '1.4' min_version = '1.4'
max_version = '9.0.3' max_version = '9.0.3'
cur_version = __salt__['pip.version'](bin_env)
too_low = salt.utils.compare_versions(ver1=cur_version, oper='<', ver2=min_version) too_low = salt.utils.compare_versions(ver1=cur_version, oper='<', ver2=min_version)
too_high = salt.utils.compare_versions(ver1=cur_version, oper='>', ver2=max_version) too_high = salt.utils.compare_versions(ver1=cur_version, oper='>', ver2=max_version)
if too_low or too_high: if too_low or too_high:
@ -637,7 +639,6 @@ def installed(name,
if no_use_wheel: if no_use_wheel:
min_version = '1.4' min_version = '1.4'
max_version = '9.0.3' max_version = '9.0.3'
cur_version = __salt__['pip.version'](bin_env)
too_low = salt.utils.compare_versions(ver1=cur_version, oper='<', ver2=min_version) too_low = salt.utils.compare_versions(ver1=cur_version, oper='<', ver2=min_version)
too_high = salt.utils.compare_versions(ver1=cur_version, oper='>', ver2=max_version) too_high = salt.utils.compare_versions(ver1=cur_version, oper='>', ver2=max_version)
if too_low or too_high: if too_low or too_high:
@ -650,7 +651,6 @@ def installed(name,
# Check that the pip binary supports the 'no_binary' option # Check that the pip binary supports the 'no_binary' option
if no_binary: if no_binary:
min_version = '7.0.0' min_version = '7.0.0'
cur_version = __salt__['pip.version'](bin_env)
too_low = salt.utils.compare_versions(ver1=cur_version, oper='<', ver2=min_version) too_low = salt.utils.compare_versions(ver1=cur_version, oper='<', ver2=min_version)
if too_low: if too_low:
ret['result'] = False ret['result'] = False
@ -723,7 +723,8 @@ def installed(name,
version_spec = version_spec version_spec = version_spec
out = _check_if_installed(prefix, state_pkg_name, version_spec, out = _check_if_installed(prefix, state_pkg_name, version_spec,
ignore_installed, force_reinstall, ignore_installed, force_reinstall,
upgrade, user, cwd, bin_env, env_vars) upgrade, user, cwd, bin_env, env_vars,
**kwargs)
# If _check_if_installed result is None, something went wrong with # If _check_if_installed result is None, something went wrong with
# the command running. This way we keep stateful output. # the command running. This way we keep stateful output.
if out['result'] is None: if out['result'] is None:
@ -803,7 +804,6 @@ def installed(name,
install_options=install_options, install_options=install_options,
global_options=global_options, global_options=global_options,
user=user, user=user,
no_chown=no_chown,
cwd=cwd, cwd=cwd,
pre_releases=pre_releases, pre_releases=pre_releases,
cert=cert, cert=cert,
@ -815,7 +815,8 @@ def installed(name,
env_vars=env_vars, env_vars=env_vars,
use_vt=use_vt, use_vt=use_vt,
trusted_host=trusted_host, trusted_host=trusted_host,
no_cache_dir=no_cache_dir no_cache_dir=no_cache_dir,
**kwargs
) )
if pip_install_call and pip_install_call.get('retcode', 1) == 0: if pip_install_call and pip_install_call.get('retcode', 1) == 0:
@ -871,7 +872,8 @@ def installed(name,
if prefix: if prefix:
pipsearch = __salt__['pip.list'](prefix, bin_env, pipsearch = __salt__['pip.list'](prefix, bin_env,
user=user, cwd=cwd, user=user, cwd=cwd,
env_vars=env_vars) env_vars=env_vars,
**kwargs)
# If we didn't find the package in the system after # If we didn't find the package in the system after
# installing it report it # installing it report it

View File

@ -39,7 +39,6 @@ def managed(name,
never_download=None, never_download=None,
prompt=None, prompt=None,
user=None, user=None,
no_chown=False,
cwd=None, cwd=None,
index_url=None, index_url=None,
extra_index_url=None, extra_index_url=None,
@ -58,7 +57,8 @@ def managed(name,
pip_no_cache_dir=False, pip_no_cache_dir=False,
pip_cache_dir=None, pip_cache_dir=None,
process_dependency_links=False, process_dependency_links=False,
no_binary=None): no_binary=None,
**kwargs):
''' '''
Create a virtualenv and optionally manage it with pip Create a virtualenv and optionally manage it with pip
@ -78,11 +78,6 @@ def managed(name,
user: None user: None
The user under which to run virtualenv and pip. The user under which to run virtualenv and pip.
no_chown: False
When user is given, do not attempt to copy and chown a requirements file
(needed if the requirements file refers to other files via relative
paths, as the copy-and-chown procedure does not account for such files)
cwd: None cwd: None
Path to the working directory where `pip install` is executed. Path to the working directory where `pip install` is executed.
@ -133,6 +128,13 @@ def managed(name,
- env_vars: - env_vars:
PATH_VAR: '/usr/local/bin/' PATH_VAR: '/usr/local/bin/'
''' '''
if 'no_chown' in kwargs:
salt.utils.warn_until(
'Flourine',
'The no_chown argument has been deprecated and is no longer used. '
'Its functionality was removed in Boron.')
kwargs.pop('no_chown')
ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} ret = {'name': name, 'result': True, 'comment': '', 'changes': {}}
if 'virtualenv.create' not in __salt__: if 'virtualenv.create' not in __salt__:
@ -205,6 +207,7 @@ def managed(name,
prompt=prompt, prompt=prompt,
user=user, user=user,
use_vt=use_vt, use_vt=use_vt,
**kwargs
) )
except CommandNotFoundError as err: except CommandNotFoundError as err:
ret['result'] = False ret['result'] = False
@ -305,7 +308,6 @@ def managed(name,
extra_index_url=extra_index_url, extra_index_url=extra_index_url,
download=pip_download, download=pip_download,
download_cache=pip_download_cache, download_cache=pip_download_cache,
no_chown=no_chown,
pre_releases=pre_releases, pre_releases=pre_releases,
exists_action=pip_exists_action, exists_action=pip_exists_action,
ignore_installed=pip_ignore_installed, ignore_installed=pip_ignore_installed,
@ -315,7 +317,8 @@ def managed(name,
use_vt=use_vt, use_vt=use_vt,
env_vars=env_vars, env_vars=env_vars,
no_cache_dir=pip_no_cache_dir, no_cache_dir=pip_no_cache_dir,
cache_dir=pip_cache_dir cache_dir=pip_cache_dir,
**kwargs
) )
ret['result'] &= pip_ret['retcode'] == 0 ret['result'] &= pip_ret['retcode'] == 0
if pip_ret['retcode'] > 0: if pip_ret['retcode'] > 0:

View File

@ -9,9 +9,9 @@
# Import python libs # Import python libs
from __future__ import absolute_import from __future__ import absolute_import
import os import os
import pwd
import shutil
import re import re
import shutil
import sys
import tempfile import tempfile
# Import Salt Testing libs # Import Salt Testing libs
@ -73,23 +73,34 @@ class PipModuleTest(ModuleCase):
# Let's remove the pip binary # Let's remove the pip binary
pip_bin = os.path.join(self.venv_dir, 'bin', 'pip') pip_bin = os.path.join(self.venv_dir, 'bin', 'pip')
py_dir = 'python{0}.{1}'.format(*sys.version_info[:2])
site_dir = os.path.join(self.venv_dir, 'lib', py_dir, 'site-packages')
if salt.utils.is_windows():
pip_bin = os.path.join(self.venv_dir, 'Scripts', 'pip.exe')
site_dir = os.path.join(self.venv_dir, 'lib', 'site-packages')
if not os.path.isfile(pip_bin): if not os.path.isfile(pip_bin):
self.skipTest( self.skipTest(
'Failed to find the pip binary to the test virtualenv' 'Failed to find the pip binary to the test virtualenv'
) )
os.remove(pip_bin) os.remove(pip_bin)
# Also remove the pip dir from site-packages
# This is needed now that we're using python -m pip instead of the
# pip binary directly. python -m pip will still work even if the
# pip binary is missing
shutil.rmtree(os.path.join(site_dir, 'pip'))
# Let's run a pip depending functions # Let's run a pip depending functions
for func in ('pip.freeze', 'pip.list'): for func in ('pip.freeze', 'pip.list'):
ret = self.run_function(func, bin_env=self.venv_dir) ret = self.run_function(func, bin_env=self.venv_dir)
self.assertIn( self.assertIn(
'Command required for \'{0}\' not found: ' 'Command required for \'{0}\' not found: '
'Could not find a `pip` binary in virtualenv'.format(func), 'Could not find a `pip` binary'.format(func),
ret ret
) )
@skip_if_not_root @skip_if_not_root
def test_requirements_as_list_of_chains__sans_no_chown__cwd_set__absolute_file_path(self): def test_requirements_as_list_of_chains__cwd_set__absolute_file_path(self):
self.run_function('virtualenv.create', [self.venv_dir]) self.run_function('virtualenv.create', [self.venv_dir])
# Create a requirements file that depends on another one. # Create a requirements file that depends on another one.
@ -108,11 +119,10 @@ class PipModuleTest(ModuleCase):
with salt.utils.fopen(req2b_filename, 'w') as f: with salt.utils.fopen(req2b_filename, 'w') as f:
f.write('pep8\n') f.write('pep8\n')
this_user = pwd.getpwuid(os.getuid())[0]
requirements_list = [req1_filename, req2_filename] requirements_list = [req1_filename, req2_filename]
ret = self.run_function( ret = self.run_function(
'pip.install', requirements=requirements_list, user=this_user, 'pip.install', requirements=requirements_list,
bin_env=self.venv_dir, cwd=self.venv_dir bin_env=self.venv_dir, cwd=self.venv_dir
) )
try: try:
@ -127,7 +137,7 @@ class PipModuleTest(ModuleCase):
raise raise
@skip_if_not_root @skip_if_not_root
def test_requirements_as_list_of_chains__sans_no_chown__cwd_not_set__absolute_file_path(self): def test_requirements_as_list_of_chains__cwd_not_set__absolute_file_path(self):
self.run_function('virtualenv.create', [self.venv_dir]) self.run_function('virtualenv.create', [self.venv_dir])
# Create a requirements file that depends on another one. # Create a requirements file that depends on another one.
@ -146,12 +156,10 @@ class PipModuleTest(ModuleCase):
with salt.utils.fopen(req2b_filename, 'w') as f: with salt.utils.fopen(req2b_filename, 'w') as f:
f.write('pep8\n') f.write('pep8\n')
this_user = pwd.getpwuid(os.getuid())[0]
requirements_list = [req1_filename, req2_filename] requirements_list = [req1_filename, req2_filename]
ret = self.run_function( ret = self.run_function(
'pip.install', requirements=requirements_list, user=this_user, 'pip.install', requirements=requirements_list, bin_env=self.venv_dir
bin_env=self.venv_dir
) )
try: try:
self.assertEqual(ret['retcode'], 0) self.assertEqual(ret['retcode'], 0)
@ -166,7 +174,7 @@ class PipModuleTest(ModuleCase):
raise raise
@skip_if_not_root @skip_if_not_root
def test_requirements_as_list__sans_no_chown__absolute_file_path(self): def test_requirements_as_list__absolute_file_path(self):
self.run_function('virtualenv.create', [self.venv_dir]) self.run_function('virtualenv.create', [self.venv_dir])
req1_filename = os.path.join(self.venv_dir, 'requirements.txt') req1_filename = os.path.join(self.venv_dir, 'requirements.txt')
@ -177,12 +185,10 @@ class PipModuleTest(ModuleCase):
with salt.utils.fopen(req2_filename, 'w') as f: with salt.utils.fopen(req2_filename, 'w') as f:
f.write('pep8\n') f.write('pep8\n')
this_user = pwd.getpwuid(os.getuid())[0]
requirements_list = [req1_filename, req2_filename] requirements_list = [req1_filename, req2_filename]
ret = self.run_function( ret = self.run_function(
'pip.install', requirements=requirements_list, user=this_user, 'pip.install', requirements=requirements_list, bin_env=self.venv_dir
bin_env=self.venv_dir
) )
found = self.pip_successful_install(ret['stdout']) found = self.pip_successful_install(ret['stdout'])
@ -197,7 +203,7 @@ class PipModuleTest(ModuleCase):
raise raise
@skip_if_not_root @skip_if_not_root
def test_requirements_as_list__sans_no_chown__non_absolute_file_path(self): def test_requirements_as_list__non_absolute_file_path(self):
self.run_function('virtualenv.create', [self.venv_dir]) self.run_function('virtualenv.create', [self.venv_dir])
# Create a requirements file that depends on another one. # Create a requirements file that depends on another one.
@ -214,11 +220,10 @@ class PipModuleTest(ModuleCase):
with salt.utils.fopen(req2_filepath, 'w') as f: with salt.utils.fopen(req2_filepath, 'w') as f:
f.write('pep8\n') f.write('pep8\n')
this_user = pwd.getpwuid(os.getuid())[0]
requirements_list = [req1_filename, req2_filename] requirements_list = [req1_filename, req2_filename]
ret = self.run_function( ret = self.run_function(
'pip.install', requirements=requirements_list, user=this_user, 'pip.install', requirements=requirements_list,
bin_env=self.venv_dir, cwd=req_cwd bin_env=self.venv_dir, cwd=req_cwd
) )
try: try:
@ -233,7 +238,7 @@ class PipModuleTest(ModuleCase):
raise raise
@skip_if_not_root @skip_if_not_root
def test_chained_requirements__sans_no_chown__absolute_file_path(self): def test_chained_requirements__absolute_file_path(self):
self.run_function('virtualenv.create', [self.venv_dir]) self.run_function('virtualenv.create', [self.venv_dir])
# Create a requirements file that depends on another one. # Create a requirements file that depends on another one.
@ -246,10 +251,8 @@ class PipModuleTest(ModuleCase):
with salt.utils.fopen(req2_filename, 'w') as f: with salt.utils.fopen(req2_filename, 'w') as f:
f.write('pep8') f.write('pep8')
this_user = pwd.getpwuid(os.getuid())[0]
ret = self.run_function( ret = self.run_function(
'pip.install', requirements=req1_filename, user=this_user, 'pip.install', requirements=req1_filename, bin_env=self.venv_dir
bin_env=self.venv_dir
) )
try: try:
self.assertEqual(ret['retcode'], 0) self.assertEqual(ret['retcode'], 0)
@ -260,7 +263,7 @@ class PipModuleTest(ModuleCase):
raise raise
@skip_if_not_root @skip_if_not_root
def test_chained_requirements__sans_no_chown__non_absolute_file_path(self): def test_chained_requirements__non_absolute_file_path(self):
self.run_function('virtualenv.create', [self.venv_dir]) self.run_function('virtualenv.create', [self.venv_dir])
# Create a requirements file that depends on another one. # Create a requirements file that depends on another one.
@ -277,10 +280,9 @@ class PipModuleTest(ModuleCase):
with salt.utils.fopen(req2_file, 'w') as f: with salt.utils.fopen(req2_file, 'w') as f:
f.write('pep8') f.write('pep8')
this_user = pwd.getpwuid(os.getuid())[0]
ret = self.run_function( ret = self.run_function(
'pip.install', requirements=req1_filename, user=this_user, 'pip.install', requirements=req1_filename, cwd=req_basepath,
no_chown=False, cwd=req_basepath, bin_env=self.venv_dir bin_env=self.venv_dir
) )
try: try:
self.assertEqual(ret['retcode'], 0) self.assertEqual(ret['retcode'], 0)
@ -291,7 +293,7 @@ class PipModuleTest(ModuleCase):
raise raise
@skip_if_not_root @skip_if_not_root
def test_issue_4805_nested_requirements_user_no_chown(self): def test_issue_4805_nested_requirements(self):
self.run_function('virtualenv.create', [self.venv_dir]) self.run_function('virtualenv.create', [self.venv_dir])
# Create a requirements file that depends on another one. # Create a requirements file that depends on another one.
@ -302,11 +304,8 @@ class PipModuleTest(ModuleCase):
with salt.utils.fopen(req2_filename, 'w') as f: with salt.utils.fopen(req2_filename, 'w') as f:
f.write('pep8') f.write('pep8')
this_user = pwd.getpwuid(os.getuid())[0]
ret = self.run_function( ret = self.run_function(
'pip.install', requirements=req1_filename, user=this_user, 'pip.install', requirements=req1_filename, bin_env=self.venv_dir)
no_chown=True, bin_env=self.venv_dir
)
if self._check_download_error(ret['stdout']): if self._check_download_error(ret['stdout']):
self.skipTest('Test skipped due to pip download error') self.skipTest('Test skipped due to pip download error')
try: try:
@ -435,9 +434,9 @@ class PipModuleTest(ModuleCase):
def tearDown(self): def tearDown(self):
super(PipModuleTest, self).tearDown() super(PipModuleTest, self).tearDown()
if os.path.isdir(self.venv_test_dir): if os.path.isdir(self.venv_test_dir):
shutil.rmtree(self.venv_test_dir) shutil.rmtree(self.venv_test_dir, ignore_errors=True)
if os.path.isdir(self.pip_temp): if os.path.isdir(self.pip_temp):
shutil.rmtree(self.pip_temp) shutil.rmtree(self.pip_temp, ignore_errors=True)
del self.venv_dir del self.venv_dir
del self.venv_test_dir del self.venv_test_dir
del self.pip_temp del self.pip_temp

View File

@ -11,11 +11,16 @@
from __future__ import absolute_import from __future__ import absolute_import
import errno import errno
import os import os
import pwd
import glob import glob
import shutil import shutil
import sys import sys
try:
import pwd
HAS_PWD = True
except ImportError:
HAS_PWD = False
# Import Salt Testing libs # Import Salt Testing libs
from tests.support.mixins import SaltReturnAssertsMixin from tests.support.mixins import SaltReturnAssertsMixin
from tests.support.unit import skipIf from tests.support.unit import skipIf
@ -24,11 +29,14 @@ from tests.support.helpers import (
destructiveTest, destructiveTest,
requires_system_grains, requires_system_grains,
with_system_user, with_system_user,
skip_if_not_root skip_if_not_root,
with_tempdir
) )
# Import salt libs # Import salt libs
from tests.support.case import ModuleCase from tests.support.case import ModuleCase
import salt.utils import salt.utils
import salt.utils.win_dacl
import salt.utils.win_functions
from salt.modules.virtualenv_mod import KNOWN_BINARY_NAMES from salt.modules.virtualenv_mod import KNOWN_BINARY_NAMES
from salt.exceptions import CommandExecutionError from salt.exceptions import CommandExecutionError
@ -47,7 +55,7 @@ class VirtualEnv(object):
def __exit__(self, exc_type, exc_value, traceback): def __exit__(self, exc_type, exc_value, traceback):
if os.path.isdir(self.venv_dir): if os.path.isdir(self.venv_dir):
shutil.rmtree(self.venv_dir) shutil.rmtree(self.venv_dir, ignore_errors=True)
@skipIf(salt.utils.which_bin(KNOWN_BINARY_NAMES) is None, 'virtualenv not installed') @skipIf(salt.utils.which_bin(KNOWN_BINARY_NAMES) is None, 'virtualenv not installed')
@ -113,9 +121,10 @@ class PipStateTest(ModuleCase, SaltReturnAssertsMixin):
else: else:
os.environ['SHELL'] = orig_shell os.environ['SHELL'] = orig_shell
if os.path.isdir(venv_dir): if os.path.isdir(venv_dir):
shutil.rmtree(venv_dir) shutil.rmtree(venv_dir, ignore_errors=True)
@skipIf(six.PY3, 'Issue is specific to carbon module, which is PY2-only') @skipIf(six.PY3, 'Issue is specific to carbon module, which is PY2-only')
@skipIf(salt.utils.is_windows(), "Carbon does not install in Windows")
@requires_system_grains @requires_system_grains
def test_pip_installed_weird_install(self, grains=None): def test_pip_installed_weird_install(self, grains=None):
# First, check to see if this is running on CentOS 5 or MacOS. # First, check to see if this is running on CentOS 5 or MacOS.
@ -141,7 +150,7 @@ class PipStateTest(ModuleCase, SaltReturnAssertsMixin):
) )
finally: finally:
if os.path.isdir(ographite): if os.path.isdir(ographite):
shutil.rmtree(ographite) shutil.rmtree(ographite, ignore_errors=True)
venv_dir = os.path.join(RUNTIME_VARS.TMP, 'pip-installed-weird-install') venv_dir = os.path.join(RUNTIME_VARS.TMP, 'pip-installed-weird-install')
try: try:
@ -150,7 +159,7 @@ class PipStateTest(ModuleCase, SaltReturnAssertsMixin):
# context when running the call to state.sls that comes after. # context when running the call to state.sls that comes after.
self.run_function('saltutil.sync_modules') self.run_function('saltutil.sync_modules')
# Since we don't have the virtualenv created, pip.installed will # Since we don't have the virtualenv created, pip.installed will
# thrown and error. # throw an error.
ret = self.run_function( ret = self.run_function(
'state.sls', mods='pip-installed-weird-install' 'state.sls', mods='pip-installed-weird-install'
) )
@ -172,7 +181,7 @@ class PipStateTest(ModuleCase, SaltReturnAssertsMixin):
raise Exception('Expected state did not run') raise Exception('Expected state did not run')
finally: finally:
if os.path.isdir(ographite): if os.path.isdir(ographite):
shutil.rmtree(ographite) shutil.rmtree(ographite, ignore_errors=True)
def test_issue_2028_pip_installed_state(self): def test_issue_2028_pip_installed_state(self):
ret = self.run_function('state.sls', mods='issue-2028-pip-installed') ret = self.run_function('state.sls', mods='issue-2028-pip-installed')
@ -181,14 +190,18 @@ class PipStateTest(ModuleCase, SaltReturnAssertsMixin):
RUNTIME_VARS.TMP, 'issue-2028-pip-installed' RUNTIME_VARS.TMP, 'issue-2028-pip-installed'
) )
pep8_bin = os.path.join(venv_dir, 'bin', 'pep8')
if salt.utils.is_windows():
pep8_bin = os.path.join(venv_dir, 'Scripts', 'pep8.exe')
try: try:
self.assertSaltTrueReturn(ret) self.assertSaltTrueReturn(ret)
self.assertTrue( self.assertTrue(
os.path.isfile(os.path.join(venv_dir, 'bin', 'pep8')) os.path.isfile(pep8_bin)
) )
finally: finally:
if os.path.isdir(venv_dir): if os.path.isdir(venv_dir):
shutil.rmtree(venv_dir) shutil.rmtree(venv_dir, ignore_errors=True)
def test_issue_2087_missing_pip(self): def test_issue_2087_missing_pip(self):
venv_dir = os.path.join( venv_dir = os.path.join(
@ -202,12 +215,23 @@ class PipStateTest(ModuleCase, SaltReturnAssertsMixin):
# Let's remove the pip binary # Let's remove the pip binary
pip_bin = os.path.join(venv_dir, 'bin', 'pip') pip_bin = os.path.join(venv_dir, 'bin', 'pip')
py_dir = 'python{0}.{1}'.format(*sys.version_info[:2])
site_dir = os.path.join(venv_dir, 'lib', py_dir, 'site-packages')
if salt.utils.is_windows():
pip_bin = os.path.join(venv_dir, 'Scripts', 'pip.exe')
site_dir = os.path.join(venv_dir, 'lib', 'site-packages')
if not os.path.isfile(pip_bin): if not os.path.isfile(pip_bin):
self.skipTest( self.skipTest(
'Failed to find the pip binary to the test virtualenv' 'Failed to find the pip binary to the test virtualenv'
) )
os.remove(pip_bin) os.remove(pip_bin)
# Also remove the pip dir from site-packages
# This is needed now that we're using python -m pip instead of the
# pip binary directly. python -m pip will still work even if the
# pip binary is missing
shutil.rmtree(os.path.join(site_dir, 'pip'))
# Let's run the state which should fail because pip is missing # Let's run the state which should fail because pip is missing
ret = self.run_function('state.sls', mods='issue-2087-missing-pip') ret = self.run_function('state.sls', mods='issue-2087-missing-pip')
self.assertSaltFalseReturn(ret) self.assertSaltFalseReturn(ret)
@ -217,7 +241,7 @@ class PipStateTest(ModuleCase, SaltReturnAssertsMixin):
) )
finally: finally:
if os.path.isdir(venv_dir): if os.path.isdir(venv_dir):
shutil.rmtree(venv_dir) shutil.rmtree(venv_dir, ignore_errors=True)
def test_issue_5940_multiple_pip_mirrors(self): def test_issue_5940_multiple_pip_mirrors(self):
''' '''
@ -242,148 +266,99 @@ class PipStateTest(ModuleCase, SaltReturnAssertsMixin):
self.skipTest('the --mirrors arg has been deprecated and removed in pip==7.0.0') self.skipTest('the --mirrors arg has been deprecated and removed in pip==7.0.0')
finally: finally:
if os.path.isdir(venv_dir): if os.path.isdir(venv_dir):
shutil.rmtree(venv_dir) shutil.rmtree(venv_dir, ignore_errors=True)
@destructiveTest @destructiveTest
@skip_if_not_root @skip_if_not_root
@with_system_user('issue-6912', on_existing='delete', delete=True) @with_system_user('issue-6912', on_existing='delete', delete=True,
def test_issue_6912_wrong_owner(self, username): password='PassWord1!')
venv_dir = os.path.join( @with_tempdir()
RUNTIME_VARS.TMP, '6912-wrong-owner' def test_issue_6912_wrong_owner(self, temp_dir, username):
) # Setup virtual environment directory to be used throughout the test
# ----- Using runas -------------------------------------------------> venv_dir = os.path.join(temp_dir, '6912-wrong-owner')
venv_create = self.run_function(
'virtualenv.create', [venv_dir], user=username
)
if venv_create['retcode'] > 0:
self.skipTest(
'Failed to create testcase virtual environment: {0}'.format(
venv_create
)
)
# Using the package name. # The virtual environment needs to be in a location that is accessible
try: # by both the user running the test and the runas user
ret = self.run_state( if salt.utils.is_windows():
'pip.installed', name='pep8', user=username, bin_env=venv_dir salt.utils.win_dacl.set_permissions(temp_dir, username, 'full_control')
) else:
self.assertSaltTrueReturn(ret) uid = self.run_function('file.user_to_uid', [username])
uinfo = pwd.getpwnam(username) os.chown(temp_dir, uid, -1)
for globmatch in (os.path.join(venv_dir, '**', 'pep8*'),
os.path.join(venv_dir, '*', '**', 'pep8*'), # Create the virtual environment
os.path.join(venv_dir, '*', '*', '**', 'pep8*')): venv_create = self.run_function(
for path in glob.glob(globmatch): 'virtualenv.create', [venv_dir], user=username,
password='PassWord1!')
if venv_create['retcode'] > 0:
self.skipTest('Failed to create testcase virtual environment: {0}'
''.format(venv_create))
# pip install passing the package name in `name`
ret = self.run_state(
'pip.installed', name='pep8', user=username, bin_env=venv_dir,
no_cache_dir=True, password='PassWord1!')
self.assertSaltTrueReturn(ret)
if HAS_PWD:
uid = pwd.getpwnam(username).pw_uid
for globmatch in (os.path.join(venv_dir, '**', 'pep8*'),
os.path.join(venv_dir, '*', '**', 'pep8*'),
os.path.join(venv_dir, '*', '*', '**', 'pep8*')):
for path in glob.glob(globmatch):
if HAS_PWD:
self.assertEqual(uid, os.stat(path).st_uid)
elif salt.utils.is_windows():
self.assertEqual( self.assertEqual(
uinfo.pw_uid, os.stat(path).st_uid salt.utils.win_dacl.get_owner(path), username)
)
finally: @destructiveTest
if os.path.isdir(venv_dir): @skip_if_not_root
shutil.rmtree(venv_dir) @with_system_user('issue-6912', on_existing='delete', delete=True,
password='PassWord1!')
@with_tempdir()
def test_issue_6912_wrong_owner_requirements_file(self, temp_dir, username):
# Setup virtual environment directory to be used throughout the test
venv_dir = os.path.join(temp_dir, '6912-wrong-owner')
# Using a requirements file # The virtual environment needs to be in a location that is accessible
# by both the user running the test and the runas user
if salt.utils.is_windows():
salt.utils.win_dacl.set_permissions(temp_dir, username, 'full_control')
else:
uid = self.run_function('file.user_to_uid', [username])
os.chown(temp_dir, uid, -1)
# Create the virtual environment again as it should have been removed
venv_create = self.run_function( venv_create = self.run_function(
'virtualenv.create', [venv_dir], user=username 'virtualenv.create', [venv_dir], user=username,
) password='PassWord1!')
if venv_create['retcode'] > 0: if venv_create['retcode'] > 0:
self.skipTest( self.skipTest('failed to create testcase virtual environment: {0}'
'Failed to create testcase virtual environment: {0}'.format( ''.format(venv_create))
ret
) # pip install using a requirements file
)
req_filename = os.path.join( req_filename = os.path.join(
RUNTIME_VARS.TMP_STATE_TREE, 'issue-6912-requirements.txt' RUNTIME_VARS.TMP_STATE_TREE, 'issue-6912-requirements.txt')
)
with salt.utils.fopen(req_filename, 'wb') as reqf: with salt.utils.fopen(req_filename, 'wb') as reqf:
reqf.write(six.b('pep8')) reqf.write(six.b('pep8'))
try: ret = self.run_state(
ret = self.run_state( 'pip.installed', name='', user=username, bin_env=venv_dir,
'pip.installed', name='', user=username, bin_env=venv_dir, requirements='salt://issue-6912-requirements.txt',
requirements='salt://issue-6912-requirements.txt' no_cache_dir=True, password='PassWord1!')
) self.assertSaltTrueReturn(ret)
self.assertSaltTrueReturn(ret)
uinfo = pwd.getpwnam(username) if HAS_PWD:
for globmatch in (os.path.join(venv_dir, '**', 'pep8*'), uid = pwd.getpwnam(username).pw_uid
os.path.join(venv_dir, '*', '**', 'pep8*'), for globmatch in (os.path.join(venv_dir, '**', 'pep8*'),
os.path.join(venv_dir, '*', '*', '**', 'pep8*')): os.path.join(venv_dir, '*', '**', 'pep8*'),
for path in glob.glob(globmatch): os.path.join(venv_dir, '*', '*', '**', 'pep8*')):
for path in glob.glob(globmatch):
if HAS_PWD:
self.assertEqual(uid, os.stat(path).st_uid)
elif salt.utils.is_windows():
self.assertEqual( self.assertEqual(
uinfo.pw_uid, os.stat(path).st_uid salt.utils.win_dacl.get_owner(path), username)
)
finally:
if os.path.isdir(venv_dir):
shutil.rmtree(venv_dir)
os.unlink(req_filename)
# <---- Using runas --------------------------------------------------
# ----- Using user -------------------------------------------------->
venv_create = self.run_function(
'virtualenv.create', [venv_dir], user=username
)
if venv_create['retcode'] > 0:
self.skipTest(
'Failed to create testcase virtual environment: {0}'.format(
ret
)
)
# Using the package name
try:
ret = self.run_state(
'pip.installed', name='pep8', user=username, bin_env=venv_dir
)
self.assertSaltTrueReturn(ret)
uinfo = pwd.getpwnam(username)
for globmatch in (os.path.join(venv_dir, '**', 'pep8*'),
os.path.join(venv_dir, '*', '**', 'pep8*'),
os.path.join(venv_dir, '*', '*', '**', 'pep8*')):
for path in glob.glob(globmatch):
self.assertEqual(
uinfo.pw_uid, os.stat(path).st_uid
)
finally:
if os.path.isdir(venv_dir):
shutil.rmtree(venv_dir)
# Using a requirements file
venv_create = self.run_function(
'virtualenv.create', [venv_dir], user=username
)
if venv_create['retcode'] > 0:
self.skipTest(
'Failed to create testcase virtual environment: {0}'.format(
ret
)
)
req_filename = os.path.join(
RUNTIME_VARS.TMP_STATE_TREE, 'issue-6912-requirements.txt'
)
with salt.utils.fopen(req_filename, 'wb') as reqf:
reqf.write(six.b('pep8'))
try:
ret = self.run_state(
'pip.installed', name='', user=username, bin_env=venv_dir,
requirements='salt://issue-6912-requirements.txt'
)
self.assertSaltTrueReturn(ret)
uinfo = pwd.getpwnam(username)
for globmatch in (os.path.join(venv_dir, '**', 'pep8*'),
os.path.join(venv_dir, '*', '**', 'pep8*'),
os.path.join(venv_dir, '*', '*', '**', 'pep8*')):
for path in glob.glob(globmatch):
self.assertEqual(
uinfo.pw_uid, os.stat(path).st_uid
)
finally:
if os.path.isdir(venv_dir):
shutil.rmtree(venv_dir)
os.unlink(req_filename)
# <---- Using user ---------------------------------------------------
def test_issue_6833_pip_upgrade_pip(self): def test_issue_6833_pip_upgrade_pip(self):
# Create the testing virtualenv # Create the testing virtualenv
@ -391,6 +366,7 @@ class PipStateTest(ModuleCase, SaltReturnAssertsMixin):
RUNTIME_VARS.TMP, '6833-pip-upgrade-pip' RUNTIME_VARS.TMP, '6833-pip-upgrade-pip'
) )
ret = self.run_function('virtualenv.create', [venv_dir]) ret = self.run_function('virtualenv.create', [venv_dir])
try: try:
try: try:
self.assertEqual(ret['retcode'], 0) self.assertEqual(ret['retcode'], 0)
@ -433,18 +409,15 @@ class PipStateTest(ModuleCase, SaltReturnAssertsMixin):
) )
try: try:
self.assertSaltTrueReturn(ret) self.assertSaltTrueReturn(ret)
self.assertInSaltReturn( self.assertSaltStateChangesEqual(
'Installed', ret, {'pip==8.0.1': 'Installed'})
ret,
['changes', 'pip==8.0.1']
)
except AssertionError: except AssertionError:
import pprint import pprint
pprint.pprint(ret) pprint.pprint(ret)
raise raise
finally: finally:
if os.path.isdir(venv_dir): if os.path.isdir(venv_dir):
shutil.rmtree(venv_dir) shutil.rmtree(venv_dir, ignore_errors=True)
def test_pip_installed_specific_env(self): def test_pip_installed_specific_env(self):
# Create the testing virtualenv # Create the testing virtualenv
@ -496,16 +469,14 @@ class PipStateTest(ModuleCase, SaltReturnAssertsMixin):
) )
finally: finally:
if os.path.isdir(venv_dir): if os.path.isdir(venv_dir):
shutil.rmtree(venv_dir) shutil.rmtree(venv_dir, ignore_errors=True)
if os.path.isfile(requirements_file): if os.path.isfile(requirements_file):
os.unlink(requirements_file) os.unlink(requirements_file)
def test_22359_pip_installed_unless_does_not_trigger_warnings(self): def test_22359_pip_installed_unless_does_not_trigger_warnings(self):
# This test case should be moved to a format_call unit test specific to # This test case should be moved to a format_call unit test specific to
# the state internal keywords # the state internal keywords
venv_dir = venv_dir = os.path.join( venv_dir = os.path.join(RUNTIME_VARS.TMP, 'pip-installed-unless')
RUNTIME_VARS.TMP, 'pip-installed-unless'
)
venv_create = self.run_function('virtualenv.create', [venv_dir]) venv_create = self.run_function('virtualenv.create', [venv_dir])
if venv_create['retcode'] > 0: if venv_create['retcode'] > 0:
self.skipTest( self.skipTest(
@ -514,17 +485,21 @@ class PipStateTest(ModuleCase, SaltReturnAssertsMixin):
) )
) )
false_cmd = '/bin/false'
if salt.utils.is_windows():
false_cmd = 'exit 1 >nul'
try: try:
ret = self.run_state( ret = self.run_state(
'pip.installed', name='pep8', bin_env=venv_dir, unless='/bin/false' 'pip.installed', name='pep8', bin_env=venv_dir, unless=false_cmd
) )
self.assertSaltTrueReturn(ret) self.assertSaltTrueReturn(ret)
self.assertNotIn('warnings', next(six.itervalues(ret))) self.assertNotIn('warnings', next(six.itervalues(ret)))
finally: finally:
if os.path.isdir(venv_dir): if os.path.isdir(venv_dir):
shutil.rmtree(venv_dir) shutil.rmtree(venv_dir, ignore_errors=True)
@skipIf(sys.version_info[:2] >= (3, 6), 'Old version of virtualenv too old for python3.6') @skipIf(sys.version_info[:2] >= (3, 6), 'Old version of virtualenv too old for python3.6')
@skipIf(salt.utils.is_windows(), "Carbon does not install in Windows")
def test_46127_pip_env_vars(self): def test_46127_pip_env_vars(self):
''' '''
Test that checks if env_vars passed to pip.installed are also passed Test that checks if env_vars passed to pip.installed are also passed
@ -548,7 +523,7 @@ class PipStateTest(ModuleCase, SaltReturnAssertsMixin):
) )
finally: finally:
if os.path.isdir(ographite): if os.path.isdir(ographite):
shutil.rmtree(ographite) shutil.rmtree(ographite, ignore_errors=True)
venv_dir = os.path.join(RUNTIME_VARS.TMP, 'issue-46127-pip-env-vars') venv_dir = os.path.join(RUNTIME_VARS.TMP, 'issue-46127-pip-env-vars')
try: try:
@ -557,7 +532,7 @@ class PipStateTest(ModuleCase, SaltReturnAssertsMixin):
# context when running the call to state.sls that comes after. # context when running the call to state.sls that comes after.
self.run_function('saltutil.sync_modules') self.run_function('saltutil.sync_modules')
# Since we don't have the virtualenv created, pip.installed will # Since we don't have the virtualenv created, pip.installed will
# thrown and error. # throw an error.
ret = self.run_function( ret = self.run_function(
'state.sls', mods='issue-46127-pip-env-vars' 'state.sls', mods='issue-46127-pip-env-vars'
) )
@ -596,6 +571,6 @@ class PipStateTest(ModuleCase, SaltReturnAssertsMixin):
raise Exception('Expected state did not run') raise Exception('Expected state did not run')
finally: finally:
if os.path.isdir(ographite): if os.path.isdir(ographite):
shutil.rmtree(ographite) shutil.rmtree(ographite, ignore_errors=True)
if os.path.isdir(venv_dir): if os.path.isdir(venv_dir):
shutil.rmtree(venv_dir) shutil.rmtree(venv_dir)

View File

@ -19,6 +19,7 @@ import functools
import inspect import inspect
import logging import logging
import os import os
import shutil
import signal import signal
import socket import socket
import subprocess import subprocess
@ -52,6 +53,9 @@ from tests.support.unit import skip, _id
from tests.support.mock import patch from tests.support.mock import patch
from tests.support.paths import FILES, TMP from tests.support.paths import FILES, TMP
# Import Salt libs
import salt.utils
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -602,10 +606,10 @@ def requires_network(only_local_network=False):
return decorator return decorator
def with_system_user(username, on_existing='delete', delete=True): def with_system_user(username, on_existing='delete', delete=True, password=None):
''' '''
Create and optionally destroy a system user to be used within a test Create and optionally destroy a system user to be used within a test
case. The system user is crated using the ``user`` salt module. case. The system user is created using the ``user`` salt module.
The decorated testcase function must accept 'username' as an argument. The decorated testcase function must accept 'username' as an argument.
@ -635,7 +639,10 @@ def with_system_user(username, on_existing='delete', delete=True):
# Let's add the user to the system. # Let's add the user to the system.
log.debug('Creating system user {0!r}'.format(username)) log.debug('Creating system user {0!r}'.format(username))
create_user = cls.run_function('user.add', [username]) kwargs = {'timeout': 60}
if salt.utils.is_windows():
kwargs.update({'password': password})
create_user = cls.run_function('user.add', [username], **kwargs)
if not create_user: if not create_user:
log.debug('Failed to create system user') log.debug('Failed to create system user')
# The user was not created # The user was not created
@ -691,7 +698,7 @@ def with_system_user(username, on_existing='delete', delete=True):
finally: finally:
if delete: if delete:
delete_user = cls.run_function( delete_user = cls.run_function(
'user.delete', [username, True, True] 'user.delete', [username, True, True], timeout=60
) )
if not delete_user: if not delete_user:
if failure is None: if failure is None:
@ -992,6 +999,32 @@ def with_tempfile(func):
return wrapper return wrapper
class WithTempdir(object):
def __init__(self, **kwargs):
self.create = kwargs.pop('create', True)
if 'dir' not in kwargs:
kwargs['dir'] = TMP
self.kwargs = kwargs
def __call__(self, func):
self.func = func
return functools.wraps(func)(
lambda testcase, *args, **kwargs: self.wrap(testcase, *args, **kwargs) # pylint: disable=W0108
)
def wrap(self, testcase, *args, **kwargs):
tempdir = tempfile.mkdtemp(**self.kwargs)
if not self.create:
os.rmdir(tempdir)
try:
return self.func(testcase, tempdir, *args, **kwargs)
finally:
shutil.rmtree(tempdir, ignore_errors=True)
with_tempdir = WithTempdir
def requires_system_grains(func): def requires_system_grains(func):
''' '''
Function decorator which loads and passes the system's grains to the test Function decorator which loads and passes the system's grains to the test

View File

@ -3,6 +3,7 @@
# Import python libs # Import python libs
from __future__ import absolute_import from __future__ import absolute_import
import os import os
import sys
# Import Salt Testing libs # Import Salt Testing libs
from tests.support.mixins import LoaderModuleMockMixin from tests.support.mixins import LoaderModuleMockMixin
@ -26,9 +27,8 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install(requirements='requirements.txt') pip.install(requirements='requirements.txt')
expected_cmd = ['pip', 'install', '--requirement', expected_cmd = [sys.executable, '-m', 'pip', 'install', '--requirement', 'requirements.txt']
'requirements.txt'] mock.assert_called_with(
mock.assert_called_once_with(
expected_cmd, expected_cmd,
saltenv='base', saltenv='base',
runas=None, runas=None,
@ -51,7 +51,7 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
'git+https://github.com/saltstack/salt-testing.git#egg=SaltTesting' 'git+https://github.com/saltstack/salt-testing.git#egg=SaltTesting'
] ]
expected = ['pip', 'install'] expected = [sys.executable, '-m', 'pip', 'install']
for item in editables: for item in editables:
expected.extend(['--editable', item]) expected.extend(['--editable', item])
@ -59,7 +59,7 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install(editable=editables) pip.install(editable=editables)
mock.assert_called_once_with( mock.assert_called_with(
expected, expected,
saltenv='base', saltenv='base',
runas=None, runas=None,
@ -71,7 +71,7 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install(editable=','.join(editables)) pip.install(editable=','.join(editables))
mock.assert_called_once_with( mock.assert_called_with(
expected, expected,
saltenv='base', saltenv='base',
runas=None, runas=None,
@ -86,7 +86,8 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
'git+https://github.com/saltstack/salt-testing.git#egg=SaltTesting' 'git+https://github.com/saltstack/salt-testing.git#egg=SaltTesting'
] ]
expected = ['pip', 'install'] + pkgs expected = [sys.executable, '-m', 'pip', 'install']
expected.extend(pkgs)
for item in editables: for item in editables:
expected.extend(['--editable', item]) expected.extend(['--editable', item])
@ -94,7 +95,7 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install(pkgs=pkgs, editable=editables) pip.install(pkgs=pkgs, editable=editables)
mock.assert_called_once_with( mock.assert_called_with(
expected, expected,
saltenv='base', saltenv='base',
runas=None, runas=None,
@ -106,7 +107,7 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install(pkgs=','.join(pkgs), editable=','.join(editables)) pip.install(pkgs=','.join(pkgs), editable=','.join(editables))
mock.assert_called_once_with( mock.assert_called_with(
expected, expected,
saltenv='base', saltenv='base',
runas=None, runas=None,
@ -118,8 +119,9 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install(pkgs=pkgs[0], editable=editables[0]) pip.install(pkgs=pkgs[0], editable=editables[0])
mock.assert_called_once_with( expected = [sys.executable, '-m', 'pip', 'install', pkgs[0], '--editable', editables[0]]
['pip', 'install', pkgs[0], '--editable', editables[0]], mock.assert_called_with(
expected,
saltenv='base', saltenv='base',
runas=None, runas=None,
use_vt=False, use_vt=False,
@ -137,7 +139,7 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
'http://pypi.crate.io' 'http://pypi.crate.io'
] ]
expected = ['pip', 'install', '--use-mirrors'] expected = [sys.executable, '-m', 'pip', 'install', '--use-mirrors']
for item in mirrors: for item in mirrors:
expected.extend(['--mirrors', item]) expected.extend(['--mirrors', item])
expected.append('pep8') expected.append('pep8')
@ -146,7 +148,7 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install(pkgs=['pep8'], mirrors=mirrors) pip.install(pkgs=['pep8'], mirrors=mirrors)
mock.assert_called_once_with( mock.assert_called_with(
expected, expected,
saltenv='base', saltenv='base',
runas=None, runas=None,
@ -158,7 +160,7 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install(pkgs=['pep8'], mirrors=','.join(mirrors)) pip.install(pkgs=['pep8'], mirrors=','.join(mirrors))
mock.assert_called_once_with( mock.assert_called_with(
expected, expected,
saltenv='base', saltenv='base',
runas=None, runas=None,
@ -166,12 +168,14 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
python_shell=False, python_shell=False,
) )
expected = [sys.executable, '-m', 'pip', 'install', '--use-mirrors', '--mirrors', mirrors[0], 'pep8']
# As single string (just use the first element from mirrors) # As single string (just use the first element from mirrors)
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install(pkgs=['pep8'], mirrors=mirrors[0]) pip.install(pkgs=['pep8'], mirrors=mirrors[0])
mock.assert_called_once_with( mock.assert_called_with(
['pip', 'install', '--use-mirrors', '--mirrors', mirrors[0], 'pep8'], expected,
saltenv='base', saltenv='base',
runas=None, runas=None,
use_vt=False, use_vt=False,
@ -186,7 +190,7 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
] ]
pkg = 'pep8' pkg = 'pep8'
expected = ['pip', 'install'] expected = [sys.executable, '-m', 'pip', 'install']
for item in find_links: for item in find_links:
expected.extend(['--find-links', item]) expected.extend(['--find-links', item])
expected.append(pkg) expected.append(pkg)
@ -195,7 +199,7 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install(pkg, find_links=find_links) pip.install(pkg, find_links=find_links)
mock.assert_called_once_with( mock.assert_called_with(
expected, expected,
saltenv='base', saltenv='base',
runas=None, runas=None,
@ -207,7 +211,7 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install(pkg, find_links=','.join(find_links)) pip.install(pkg, find_links=','.join(find_links))
mock.assert_called_once_with( mock.assert_called_with(
expected, expected,
saltenv='base', saltenv='base',
runas=None, runas=None,
@ -215,12 +219,26 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
python_shell=False, python_shell=False,
) )
# Valid protos work?
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install(pkg, find_links=find_links)
mock.assert_called_with(
expected,
saltenv='base',
runas=None,
use_vt=False,
python_shell=False,
)
expected = [sys.executable, '-m', 'pip', 'install', '--find-links', find_links[0], pkg]
# As single string (just use the first element from find_links) # As single string (just use the first element from find_links)
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install(pkg, find_links=find_links[0]) pip.install(pkg, find_links=find_links[0])
mock.assert_called_once_with( mock.assert_called_with(
['pip', 'install', '--find-links', find_links[0], pkg], expected,
saltenv='base', saltenv='base',
runas=None, runas=None,
use_vt=False, use_vt=False,
@ -237,18 +255,6 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
find_links='sftp://pypi.crate.io' find_links='sftp://pypi.crate.io'
) )
# Valid protos work?
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install(pkg, find_links=find_links)
mock.assert_called_once_with(
expected,
saltenv='base',
runas=None,
use_vt=False,
python_shell=False,
)
def test_install_no_index_with_index_url_or_extra_index_url_raises(self): def test_install_no_index_with_index_url_or_extra_index_url_raises(self):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
@ -277,8 +283,8 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install(requirements='salt://requirements.txt') pip.install(requirements='salt://requirements.txt')
expected = ['pip', 'install', '--requirement', 'my_cached_reqs'] expected = [sys.executable, '-m', 'pip', 'install', '--requirement', 'my_cached_reqs']
mock.assert_called_once_with( mock.assert_called_with(
expected, expected,
saltenv='base', saltenv='base',
runas=None, runas=None,
@ -288,28 +294,29 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
def test_install_venv(self): def test_install_venv(self):
with patch('os.path') as mock_path: with patch('os.path') as mock_path:
mock_path.is_file.return_value = True
mock_path.isdir.return_value = True
pkg = 'mock'
def join(*args): def join(*args):
return os.sep.join(args) return os.path.normpath(os.sep.join(args))
mock_path.is_file.return_value = True
mock_path.isdir.return_value = True
mock_path.join = join mock_path.join = join
if salt.utils.is_windows():
venv_path = 'C:\\test_env'
bin_path = os.path.join(venv_path, 'python.exe')
else:
venv_path = '/test_env'
bin_path = os.path.join(venv_path, 'python')
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): pip_bin = MagicMock(return_value=[bin_path, '-m', 'pip'])
if salt.utils.is_windows():
venv_path = 'c:\\test_env' with patch.dict(pip.__salt__, {'cmd.run_all': mock}), \
bin_path = os.path.join(venv_path, 'Scripts', 'pip.exe') patch.object(pip, '_get_pip_bin', pip_bin):
if salt.ext.six.PY2: pip.install('mock', bin_env=venv_path)
bin_path = bin_path.encode('string-escape') mock.assert_called_with(
else: [bin_path, '-m', 'pip', 'install', 'mock'],
venv_path = '/test_env'
bin_path = os.path.join(venv_path, 'bin', 'pip')
pip.install(pkg, bin_env=venv_path)
mock.assert_called_once_with(
[bin_path, 'install', pkg],
env={'VIRTUAL_ENV': venv_path}, env={'VIRTUAL_ENV': venv_path},
saltenv='base', saltenv='base',
runas=None, runas=None,
@ -324,8 +331,9 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install(pkg, log=log_path) pip.install(pkg, log=log_path)
mock.assert_called_once_with( expected = [sys.executable, '-m', 'pip', 'install', '--log', log_path, pkg]
['pip', 'install', '--log', log_path, pkg], mock.assert_called_with(
expected,
saltenv='base', saltenv='base',
runas=None, runas=None,
use_vt=False, use_vt=False,
@ -350,12 +358,12 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
def test_install_timeout_argument_in_resulting_command(self): def test_install_timeout_argument_in_resulting_command(self):
# Passing an int # Passing an int
pkg = 'pep8' pkg = 'pep8'
expected_prefix = ['pip', 'install', '--timeout'] expected = [sys.executable, '-m', 'pip', 'install', '--timeout']
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install(pkg, timeout=10) pip.install(pkg, timeout=10)
mock.assert_called_once_with( mock.assert_called_with(
expected_prefix + [10, pkg], expected + [10, pkg],
saltenv='base', saltenv='base',
runas=None, runas=None,
use_vt=False, use_vt=False,
@ -366,8 +374,8 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install(pkg, timeout='10') pip.install(pkg, timeout='10')
mock.assert_called_once_with( mock.assert_called_with(
expected_prefix + ['10', pkg], expected + ['10', pkg],
saltenv='base', saltenv='base',
runas=None, runas=None,
use_vt=False, use_vt=False,
@ -390,8 +398,9 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install(pkg, index_url=index_url) pip.install(pkg, index_url=index_url)
mock.assert_called_once_with( expected = [sys.executable, '-m', 'pip', 'install', '--index-url', index_url, pkg]
['pip', 'install', '--index-url', index_url, pkg], mock.assert_called_with(
expected,
saltenv='base', saltenv='base',
runas=None, runas=None,
use_vt=False, use_vt=False,
@ -404,8 +413,9 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install(pkg, extra_index_url=extra_index_url) pip.install(pkg, extra_index_url=extra_index_url)
mock.assert_called_once_with( expected = [sys.executable, '-m', 'pip', 'install', '--extra-index-url', extra_index_url, pkg]
['pip', 'install', '--extra-index-url', extra_index_url, pkg], mock.assert_called_with(
expected,
saltenv='base', saltenv='base',
runas=None, runas=None,
use_vt=False, use_vt=False,
@ -417,8 +427,9 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install(pkg, no_index=True) pip.install(pkg, no_index=True)
mock.assert_called_once_with( expected = [sys.executable, '-m', 'pip', 'install', '--no-index', pkg]
['pip', 'install', '--no-index', pkg], mock.assert_called_with(
expected,
saltenv='base', saltenv='base',
runas=None, runas=None,
use_vt=False, use_vt=False,
@ -431,8 +442,9 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install(pkg, build=build) pip.install(pkg, build=build)
mock.assert_called_once_with( expected = [sys.executable, '-m', 'pip', 'install', '--build', build, pkg]
['pip', 'install', '--build', build, pkg], mock.assert_called_with(
expected,
saltenv='base', saltenv='base',
runas=None, runas=None,
use_vt=False, use_vt=False,
@ -445,8 +457,9 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install(pkg, target=target) pip.install(pkg, target=target)
mock.assert_called_once_with( expected = [sys.executable, '-m', 'pip', 'install', '--target', target, pkg]
['pip', 'install', '--target', target, pkg], mock.assert_called_with(
expected,
saltenv='base', saltenv='base',
runas=None, runas=None,
use_vt=False, use_vt=False,
@ -459,8 +472,9 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install(pkg, download=download) pip.install(pkg, download=download)
mock.assert_called_once_with( expected = [sys.executable, '-m', 'pip', 'install', '--download', download, pkg]
['pip', 'install', '--download', download, pkg], mock.assert_called_with(
expected,
saltenv='base', saltenv='base',
runas=None, runas=None,
use_vt=False, use_vt=False,
@ -472,8 +486,9 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install(pkg, no_download=True) pip.install(pkg, no_download=True)
mock.assert_called_once_with( expected = [sys.executable, '-m', 'pip', 'install', '--no-download', pkg]
['pip', 'install', '--no-download', pkg], mock.assert_called_with(
expected,
saltenv='base', saltenv='base',
runas=None, runas=None,
use_vt=False, use_vt=False,
@ -495,8 +510,9 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
MagicMock(return_value=pip_version)): MagicMock(return_value=pip_version)):
# test `download_cache` kwarg # test `download_cache` kwarg
pip.install(pkg, download_cache='/tmp/foo') pip.install(pkg, download_cache='/tmp/foo')
expected = [sys.executable, '-m', 'pip', 'install', cmd_arg, download_cache, pkg]
mock.assert_called_with( mock.assert_called_with(
['pip', 'install', cmd_arg, download_cache, pkg], expected,
saltenv='base', saltenv='base',
runas=None, runas=None,
use_vt=False, use_vt=False,
@ -506,7 +522,7 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
# test `cache_dir` kwarg # test `cache_dir` kwarg
pip.install(pkg, cache_dir='/tmp/foo') pip.install(pkg, cache_dir='/tmp/foo')
mock.assert_called_with( mock.assert_called_with(
['pip', 'install', cmd_arg, download_cache, pkg], expected,
saltenv='base', saltenv='base',
runas=None, runas=None,
use_vt=False, use_vt=False,
@ -519,8 +535,9 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install(pkg, source=source) pip.install(pkg, source=source)
mock.assert_called_once_with( expected = [sys.executable, '-m', 'pip', 'install', '--source', source, pkg]
['pip', 'install', '--source', source, pkg], mock.assert_called_with(
expected,
saltenv='base', saltenv='base',
runas=None, runas=None,
use_vt=False, use_vt=False,
@ -532,9 +549,10 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
for action in ('s', 'i', 'w', 'b'): for action in ('s', 'i', 'w', 'b'):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install('pep8', exists_action=action) pip.install(pkg, exists_action=action)
mock.assert_called_once_with( expected = [sys.executable, '-m', 'pip', 'install', '--exists-action', action, pkg]
['pip', 'install', '--exists-action', action, pkg], mock.assert_called_with(
expected,
saltenv='base', saltenv='base',
runas=None, runas=None,
use_vt=False, use_vt=False,
@ -558,7 +576,7 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
] ]
pkg = 'pep8' pkg = 'pep8'
expected = ['pip', 'install'] expected = [sys.executable, '-m', 'pip', 'install']
for item in install_options: for item in install_options:
expected.extend(['--install-option', item]) expected.extend(['--install-option', item])
expected.append(pkg) expected.append(pkg)
@ -567,7 +585,7 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install(pkg, install_options=install_options) pip.install(pkg, install_options=install_options)
mock.assert_called_once_with( mock.assert_called_with(
expected, expected,
saltenv='base', saltenv='base',
runas=None, runas=None,
@ -579,7 +597,7 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install(pkg, install_options=','.join(install_options)) pip.install(pkg, install_options=','.join(install_options))
mock.assert_called_once_with( mock.assert_called_with(
expected, expected,
saltenv='base', saltenv='base',
runas=None, runas=None,
@ -591,9 +609,9 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install(pkg, install_options=install_options[0]) pip.install(pkg, install_options=install_options[0])
mock.assert_called_once_with( expected = [sys.executable, '-m', 'pip', 'install', '--install-option', install_options[0], pkg]
['pip', 'install', '--install-option', mock.assert_called_with(
install_options[0], pkg], expected,
saltenv='base', saltenv='base',
runas=None, runas=None,
use_vt=False, use_vt=False,
@ -607,7 +625,7 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
] ]
pkg = 'pep8' pkg = 'pep8'
expected = ['pip', 'install'] expected = [sys.executable, '-m', 'pip', 'install']
for item in global_options: for item in global_options:
expected.extend(['--global-option', item]) expected.extend(['--global-option', item])
expected.append(pkg) expected.append(pkg)
@ -616,7 +634,7 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install(pkg, global_options=global_options) pip.install(pkg, global_options=global_options)
mock.assert_called_once_with( mock.assert_called_with(
expected, expected,
saltenv='base', saltenv='base',
runas=None, runas=None,
@ -628,7 +646,7 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install(pkg, global_options=','.join(global_options)) pip.install(pkg, global_options=','.join(global_options))
mock.assert_called_once_with( mock.assert_called_with(
expected, expected,
saltenv='base', saltenv='base',
runas=None, runas=None,
@ -640,8 +658,9 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install(pkg, global_options=global_options[0]) pip.install(pkg, global_options=global_options[0])
mock.assert_called_once_with( expected = [sys.executable, '-m', 'pip', 'install', '--global-option', global_options[0], pkg]
['pip', 'install', '--global-option', global_options[0], pkg], mock.assert_called_with(
expected,
saltenv='base', saltenv='base',
runas=None, runas=None,
use_vt=False, use_vt=False,
@ -653,8 +672,9 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install(pkg, upgrade=True) pip.install(pkg, upgrade=True)
mock.assert_called_once_with( expected = [sys.executable, '-m', 'pip', 'install', '--upgrade', pkg]
['pip', 'install', '--upgrade', pkg], mock.assert_called_with(
expected,
saltenv='base', saltenv='base',
runas=None, runas=None,
use_vt=False, use_vt=False,
@ -666,8 +686,9 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install(pkg, force_reinstall=True) pip.install(pkg, force_reinstall=True)
mock.assert_called_once_with( expected = [sys.executable, '-m', 'pip', 'install', '--force-reinstall', pkg]
['pip', 'install', '--force-reinstall', pkg], mock.assert_called_with(
expected,
saltenv='base', saltenv='base',
runas=None, runas=None,
use_vt=False, use_vt=False,
@ -679,8 +700,9 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install(pkg, ignore_installed=True) pip.install(pkg, ignore_installed=True)
mock.assert_called_once_with( expected = [sys.executable, '-m', 'pip', 'install', '--ignore-installed', pkg]
['pip', 'install', '--ignore-installed', pkg], mock.assert_called_with(
expected,
saltenv='base', saltenv='base',
runas=None, runas=None,
use_vt=False, use_vt=False,
@ -692,8 +714,9 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install(pkg, no_deps=True) pip.install(pkg, no_deps=True)
mock.assert_called_once_with( expected = [sys.executable, '-m', 'pip', 'install', '--no-deps', pkg]
['pip', 'install', '--no-deps', pkg], mock.assert_called_with(
expected,
saltenv='base', saltenv='base',
runas=None, runas=None,
use_vt=False, use_vt=False,
@ -705,8 +728,9 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install(pkg, no_install=True) pip.install(pkg, no_install=True)
mock.assert_called_once_with( expected = [sys.executable, '-m', 'pip', 'install', '--no-install', pkg]
['pip', 'install', '--no-install', pkg], mock.assert_called_with(
expected,
saltenv='base', saltenv='base',
runas=None, runas=None,
use_vt=False, use_vt=False,
@ -719,8 +743,9 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install(pkg, proxy=proxy) pip.install(pkg, proxy=proxy)
mock.assert_called_once_with( expected = [sys.executable, '-m', 'pip', 'install', '--proxy', proxy, pkg]
['pip', 'install', '--proxy', proxy, pkg], mock.assert_called_with(
expected,
saltenv='base', saltenv='base',
runas=None, runas=None,
use_vt=False, use_vt=False,
@ -737,7 +762,7 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
'salt://requirements-1.txt', 'salt://requirements-2.txt' 'salt://requirements-1.txt', 'salt://requirements-2.txt'
] ]
expected = ['pip', 'install'] expected = [sys.executable, '-m', 'pip', 'install']
for item in cached_reqs: for item in cached_reqs:
expected.extend(['--requirement', item]) expected.extend(['--requirement', item])
@ -745,7 +770,7 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install(requirements=requirements) pip.install(requirements=requirements)
mock.assert_called_once_with( mock.assert_called_with(
expected, expected,
saltenv='base', saltenv='base',
runas=None, runas=None,
@ -758,7 +783,7 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install(requirements=','.join(requirements)) pip.install(requirements=','.join(requirements))
mock.assert_called_once_with( mock.assert_called_with(
expected, expected,
saltenv='base', saltenv='base',
runas=None, runas=None,
@ -771,8 +796,9 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.install(requirements=requirements[0]) pip.install(requirements=requirements[0])
mock.assert_called_once_with( expected = [sys.executable, '-m', 'pip', 'install', '--requirement', cached_reqs[0]]
['pip', 'install', '--requirement', cached_reqs[0]], mock.assert_called_with(
expected,
saltenv='base', saltenv='base',
runas=None, runas=None,
use_vt=False, use_vt=False,
@ -789,7 +815,7 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
'salt://requirements-1.txt', 'salt://requirements-2.txt' 'salt://requirements-1.txt', 'salt://requirements-2.txt'
] ]
expected = ['pip', 'uninstall', '-y'] expected = [sys.executable, '-m', 'pip', 'uninstall', '-y']
for item in cached_reqs: for item in cached_reqs:
expected.extend(['--requirement', item]) expected.extend(['--requirement', item])
@ -797,7 +823,7 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.uninstall(requirements=requirements) pip.uninstall(requirements=requirements)
mock.assert_called_once_with( mock.assert_called_with(
expected, expected,
cwd=None, cwd=None,
saltenv='base', saltenv='base',
@ -811,7 +837,7 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.uninstall(requirements=','.join(requirements)) pip.uninstall(requirements=','.join(requirements))
mock.assert_called_once_with( mock.assert_called_with(
expected, expected,
cwd=None, cwd=None,
saltenv='base', saltenv='base',
@ -825,8 +851,9 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.uninstall(requirements=requirements[0]) pip.uninstall(requirements=requirements[0])
mock.assert_called_once_with( expected = [sys.executable, '-m', 'pip', 'uninstall', '-y', '--requirement', cached_reqs[0]]
['pip', 'uninstall', '-y', '--requirement', cached_reqs[0]], mock.assert_called_with(
expected,
cwd=None, cwd=None,
saltenv='base', saltenv='base',
runas=None, runas=None,
@ -840,8 +867,9 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.uninstall(pkg, proxy=proxy) pip.uninstall(pkg, proxy=proxy)
mock.assert_called_once_with( expected = [sys.executable, '-m', 'pip', 'uninstall', '-y', '--proxy', proxy, pkg]
['pip', 'uninstall', '-y', '--proxy', proxy, pkg], mock.assert_called_with(
expected,
saltenv='base', saltenv='base',
cwd=None, cwd=None,
runas=None, runas=None,
@ -850,22 +878,24 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
) )
def test_uninstall_log_argument_in_resulting_command(self): def test_uninstall_log_argument_in_resulting_command(self):
with patch('os.path') as mock_path: pkg = 'pep8'
pkg = 'pep8' log_path = '/tmp/pip-install.log'
log_path = '/tmp/pip-install.log'
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.uninstall(pkg, log=log_path)
mock.assert_called_once_with(
['pip', 'uninstall', '-y', '--log', log_path, pkg],
saltenv='base',
cwd=None,
runas=None,
use_vt=False,
python_shell=False,
)
# Let's fake a non-writable log file mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.uninstall(pkg, log=log_path)
expected = [sys.executable, '-m', 'pip', 'uninstall', '-y', '--log', log_path, pkg]
mock.assert_called_with(
expected,
saltenv='base',
cwd=None,
runas=None,
use_vt=False,
python_shell=False,
)
# Let's fake a non-writable log file
with patch('os.path') as mock_path:
mock_path.exists.side_effect = IOError('Fooo!') mock_path.exists.side_effect = IOError('Fooo!')
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
@ -878,13 +908,13 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
def test_uninstall_timeout_argument_in_resulting_command(self): def test_uninstall_timeout_argument_in_resulting_command(self):
pkg = 'pep8' pkg = 'pep8'
expected_prefix = ['pip', 'uninstall', '-y', '--timeout'] expected = [sys.executable, '-m', 'pip', 'uninstall', '-y', '--timeout']
# Passing an int # Passing an int
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.uninstall(pkg, timeout=10) pip.uninstall(pkg, timeout=10)
mock.assert_called_once_with( mock.assert_called_with(
expected_prefix + [10, pkg], expected + [10, pkg],
cwd=None, cwd=None,
saltenv='base', saltenv='base',
runas=None, runas=None,
@ -896,8 +926,8 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}): with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
pip.uninstall(pkg, timeout='10') pip.uninstall(pkg, timeout='10')
mock.assert_called_once_with( mock.assert_called_with(
expected_prefix + ['10', pkg], expected + ['10', pkg],
cwd=None, cwd=None,
saltenv='base', saltenv='base',
runas=None, runas=None,
@ -916,6 +946,7 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
) )
def test_freeze_command(self): def test_freeze_command(self):
expected = [sys.executable, '-m', 'pip', 'freeze']
eggs = [ eggs = [
'M2Crypto==0.21.1', 'M2Crypto==0.21.1',
'-e git+git@github.com:s0undt3ch/salt-testing.git@9ed81aa2f918d59d3706e56b18f0782d1ea43bf8#egg=SaltTesting-dev', '-e git+git@github.com:s0undt3ch/salt-testing.git@9ed81aa2f918d59d3706e56b18f0782d1ea43bf8#egg=SaltTesting-dev',
@ -933,8 +964,8 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
with patch('salt.modules.pip.version', with patch('salt.modules.pip.version',
MagicMock(return_value='6.1.1')): MagicMock(return_value='6.1.1')):
ret = pip.freeze() ret = pip.freeze()
mock.assert_called_once_with( mock.assert_called_with(
['pip', 'freeze'], expected,
cwd=None, cwd=None,
runas=None, runas=None,
use_vt=False, use_vt=False,
@ -953,8 +984,8 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
with patch('salt.modules.pip.version', with patch('salt.modules.pip.version',
MagicMock(return_value='6.1.1')): MagicMock(return_value='6.1.1')):
ret = pip.freeze(env_vars={"foo": "bar"}) ret = pip.freeze(env_vars={"foo": "bar"})
mock.assert_called_once_with( mock.assert_called_with(
['pip', 'freeze'], expected,
cwd=None, cwd=None,
runas=None, runas=None,
use_vt=False, use_vt=False,
@ -993,8 +1024,9 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
with patch('salt.modules.pip.version', with patch('salt.modules.pip.version',
MagicMock(return_value='9.0.1')): MagicMock(return_value='9.0.1')):
ret = pip.freeze() ret = pip.freeze()
mock.assert_called_once_with( expected = [sys.executable, '-m', 'pip', 'freeze', '--all']
['pip', 'freeze', '--all'], mock.assert_called_with(
expected,
cwd=None, cwd=None,
runas=None, runas=None,
use_vt=False, use_vt=False,
@ -1026,8 +1058,9 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
with patch('salt.modules.pip.version', with patch('salt.modules.pip.version',
MagicMock(return_value=mock_version)): MagicMock(return_value=mock_version)):
ret = pip.list_() ret = pip.list_()
expected = [sys.executable, '-m', 'pip', 'freeze']
mock.assert_called_with( mock.assert_called_with(
['pip', 'freeze'], expected,
cwd=None, cwd=None,
runas=None, runas=None,
python_shell=False, python_shell=False,
@ -1073,8 +1106,9 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
with patch('salt.modules.pip.version', with patch('salt.modules.pip.version',
MagicMock(return_value=mock_version)): MagicMock(return_value=mock_version)):
ret = pip.list_() ret = pip.list_()
expected = [sys.executable, '-m', 'pip', 'freeze', '--all']
mock.assert_called_with( mock.assert_called_with(
['pip', 'freeze', '--all'], expected,
cwd=None, cwd=None,
runas=None, runas=None,
python_shell=False, python_shell=False,
@ -1120,8 +1154,9 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
with patch('salt.modules.pip.version', with patch('salt.modules.pip.version',
MagicMock(return_value='6.1.1')): MagicMock(return_value='6.1.1')):
ret = pip.list_(prefix='bb') ret = pip.list_(prefix='bb')
expected = [sys.executable, '-m', 'pip', 'freeze']
mock.assert_called_with( mock.assert_called_with(
['pip', 'freeze'], expected,
cwd=None, cwd=None,
runas=None, runas=None,
python_shell=False, python_shell=False,
@ -1146,8 +1181,9 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
with patch('salt.modules.pip.version', with patch('salt.modules.pip.version',
MagicMock(return_value='1.3')): MagicMock(return_value='1.3')):
pip.install(pkg, pre_releases=True) pip.install(pkg, pre_releases=True)
expected = [sys.executable, '-m', 'pip', 'install', pkg]
mock.assert_called_with( mock.assert_called_with(
['pip', 'install', pkg], expected,
saltenv='base', saltenv='base',
runas=None, runas=None,
use_vt=False, use_vt=False,
@ -1159,10 +1195,11 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
with patch.dict(pip.__salt__, {'cmd.run': mock_run, with patch.dict(pip.__salt__, {'cmd.run': mock_run,
'cmd.run_all': mock_run_all}): 'cmd.run_all': mock_run_all}):
with patch('salt.modules.pip._get_pip_bin', with patch('salt.modules.pip._get_pip_bin',
MagicMock(return_value='pip')): MagicMock(return_value=['pip'])):
pip.install(pkg, pre_releases=True) pip.install(pkg, pre_releases=True)
expected = ['pip', 'install', '--pre', pkg]
mock_run_all.assert_called_with( mock_run_all.assert_called_with(
['pip', 'install', '--pre', pkg], expected,
saltenv='base', saltenv='base',
runas=None, runas=None,
use_vt=False, use_vt=False,

View File

@ -17,6 +17,7 @@ from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch
# Import salt libs # Import salt libs
import salt.states.pip_state as pip_state import salt.states.pip_state as pip_state
import salt.utils
# Import 3rd-party libs # Import 3rd-party libs
try: try:
@ -43,211 +44,163 @@ class PipStateTest(TestCase, SaltReturnAssertsMixin, LoaderModuleMockMixin):
def test_install_requirements_parsing(self): def test_install_requirements_parsing(self):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
pip_list = MagicMock(return_value={'pep8': '1.3.3'}) pip_list = MagicMock(return_value={'pep8': '1.3.3'})
with patch.dict(pip_state.__salt__, {'cmd.run_all': mock, pip_version = pip.__version__
'pip.list': pip_list}): mock_pip_version = MagicMock(return_value=pip_version)
with patch.dict(pip_state.__opts__, {'test': True}): with patch.dict(pip_state.__salt__, {'pip.version': mock_pip_version}):
ret = pip_state.installed('pep8=1.3.2') with patch.dict(pip_state.__salt__, {'cmd.run_all': mock,
self.assertSaltFalseReturn({'test': ret}) 'pip.list': pip_list}):
self.assertInSaltComment( with patch.dict(pip_state.__opts__, {'test': True}):
'Invalid version specification in package pep8=1.3.2. ' if salt.utils.compare_versions(ver1=pip_version,
'\'=\' is not supported, use \'==\' instead.', oper='<',
{'test': ret} ver2='10.0'):
) ret = pip_state.installed('pep8=1.3.2')
self.assertSaltFalseReturn({'test': ret})
self.assertInSaltComment(
'Invalid version specification in package pep8=1.3.2. '
'\'=\' is not supported, use \'==\' instead.',
{'test': ret})
else:
self.assertRaises(
pip._internal.exceptions.InstallationError,
pip_state.installed, 'pep=1.3.2')
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
pip_list = MagicMock(return_value={'pep8': '1.3.3'}) pip_list = MagicMock(return_value={'pep8': '1.3.3'})
pip_install = MagicMock(return_value={'retcode': 0}) pip_install = MagicMock(return_value={'retcode': 0})
with patch.dict(pip_state.__salt__, {'cmd.run_all': mock, with patch.dict(pip_state.__salt__, {'cmd.run_all': mock,
'pip.list': pip_list, 'pip.list': pip_list,
'pip.install': pip_install}): 'pip.install': pip_install}):
with patch.dict(pip_state.__opts__, {'test': True}): with patch.dict(pip_state.__opts__, {'test': True}):
ret = pip_state.installed('pep8>=1.3.2') ret = pip_state.installed('pep8>=1.3.2')
self.assertSaltTrueReturn({'test': ret}) self.assertSaltTrueReturn({'test': ret})
self.assertInSaltComment( self.assertInSaltComment(
'Python package pep8>=1.3.2 was already installed', 'Python package pep8>=1.3.2 was already installed',
{'test': ret} {'test': ret}
) )
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
pip_list = MagicMock(return_value={'pep8': '1.3.3'}) pip_list = MagicMock(return_value={'pep8': '1.3.3'})
with patch.dict(pip_state.__salt__, {'cmd.run_all': mock, with patch.dict(pip_state.__salt__, {'cmd.run_all': mock,
'pip.list': pip_list}): 'pip.list': pip_list}):
with patch.dict(pip_state.__opts__, {'test': True}): with patch.dict(pip_state.__opts__, {'test': True}):
ret = pip_state.installed('pep8<1.3.2') ret = pip_state.installed('pep8<1.3.2')
self.assertSaltNoneReturn({'test': ret}) self.assertSaltNoneReturn({'test': ret})
self.assertInSaltComment( self.assertInSaltComment(
'Python package pep8<1.3.2 is set to be installed', 'Python package pep8<1.3.2 is set to be installed',
{'test': ret} {'test': ret}
) )
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
pip_list = MagicMock(return_value={'pep8': '1.3.2'}) pip_list = MagicMock(return_value={'pep8': '1.3.2'})
pip_install = MagicMock(return_value={'retcode': 0}) pip_install = MagicMock(return_value={'retcode': 0})
with patch.dict(pip_state.__salt__, {'cmd.run_all': mock, with patch.dict(pip_state.__salt__, {'cmd.run_all': mock,
'pip.list': pip_list, 'pip.list': pip_list,
'pip.install': pip_install}): 'pip.install': pip_install}):
with patch.dict(pip_state.__opts__, {'test': True}): with patch.dict(pip_state.__opts__, {'test': True}):
ret = pip_state.installed('pep8>1.3.1,<1.3.3') ret = pip_state.installed('pep8>1.3.1,<1.3.3')
self.assertSaltTrueReturn({'test': ret}) self.assertSaltTrueReturn({'test': ret})
self.assertInSaltComment( self.assertInSaltComment(
'Python package pep8>1.3.1,<1.3.3 was already installed', 'Python package pep8>1.3.1,<1.3.3 was already installed',
{'test': ret} {'test': ret}
) )
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
pip_list = MagicMock(return_value={'pep8': '1.3.1'}) pip_list = MagicMock(return_value={'pep8': '1.3.1'})
pip_install = MagicMock(return_value={'retcode': 0}) pip_install = MagicMock(return_value={'retcode': 0})
with patch.dict(pip_state.__salt__, {'cmd.run_all': mock, with patch.dict(pip_state.__salt__, {'cmd.run_all': mock,
'pip.list': pip_list, 'pip.list': pip_list,
'pip.install': pip_install}): 'pip.install': pip_install}):
with patch.dict(pip_state.__opts__, {'test': True}): with patch.dict(pip_state.__opts__, {'test': True}):
ret = pip_state.installed('pep8>1.3.1,<1.3.3') ret = pip_state.installed('pep8>1.3.1,<1.3.3')
self.assertSaltNoneReturn({'test': ret}) self.assertSaltNoneReturn({'test': ret})
self.assertInSaltComment( self.assertInSaltComment(
'Python package pep8>1.3.1,<1.3.3 is set to be installed', 'Python package pep8>1.3.1,<1.3.3 is set to be installed',
{'test': ret} {'test': ret}
) )
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
pip_list = MagicMock(return_value={'pep8': '1.3.1'}) pip_list = MagicMock(return_value={'pep8': '1.3.1'})
with patch.dict(pip_state.__salt__, {'cmd.run_all': mock, with patch.dict(pip_state.__salt__, {'cmd.run_all': mock,
'pip.list': pip_list}): 'pip.list': pip_list}):
with patch.dict(pip_state.__opts__, {'test': True}): with patch.dict(pip_state.__opts__, {'test': True}):
ret = pip_state.installed( ret = pip_state.installed(
'git+https://github.com/saltstack/salt-testing.git#egg=SaltTesting>=0.5.1' 'git+https://github.com/saltstack/salt-testing.git#egg=SaltTesting>=0.5.1'
) )
self.assertSaltNoneReturn({'test': ret}) self.assertSaltNoneReturn({'test': ret})
self.assertInSaltComment( self.assertInSaltComment(
'Python package git+https://github.com/saltstack/' 'Python package git+https://github.com/saltstack/'
'salt-testing.git#egg=SaltTesting>=0.5.1 is set to be ' 'salt-testing.git#egg=SaltTesting>=0.5.1 is set to be '
'installed', 'installed',
{'test': ret} {'test': ret}
) )
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
pip_list = MagicMock(return_value={'pep8': '1.3.1'}) pip_list = MagicMock(return_value={'pep8': '1.3.1'})
with patch.dict(pip_state.__salt__, {'cmd.run_all': mock, with patch.dict(pip_state.__salt__, {'cmd.run_all': mock,
'pip.list': pip_list}): 'pip.list': pip_list}):
with patch.dict(pip_state.__opts__, {'test': True}): with patch.dict(pip_state.__opts__, {'test': True}):
ret = pip_state.installed( ret = pip_state.installed(
'git+https://github.com/saltstack/salt-testing.git#egg=SaltTesting' 'git+https://github.com/saltstack/salt-testing.git#egg=SaltTesting'
) )
self.assertSaltNoneReturn({'test': ret}) self.assertSaltNoneReturn({'test': ret})
self.assertInSaltComment( self.assertInSaltComment(
'Python package git+https://github.com/saltstack/' 'Python package git+https://github.com/saltstack/'
'salt-testing.git#egg=SaltTesting is set to be ' 'salt-testing.git#egg=SaltTesting is set to be '
'installed', 'installed',
{'test': ret} {'test': ret}
) )
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
pip_list = MagicMock(return_value={'pep8': '1.3.1'}) pip_list = MagicMock(return_value={'pep8': '1.3.1'})
with patch.dict(pip_state.__salt__, {'cmd.run_all': mock, with patch.dict(pip_state.__salt__, {'cmd.run_all': mock,
'pip.list': pip_list}): 'pip.list': pip_list}):
with patch.dict(pip_state.__opts__, {'test': True}): with patch.dict(pip_state.__opts__, {'test': True}):
ret = pip_state.installed(
'https://pypi.python.org/packages/source/S/SaltTesting/'
'SaltTesting-0.5.0.tar.gz'
'#md5=e6760af92b7165f8be53b5763e40bc24'
)
self.assertSaltNoneReturn({'test': ret})
self.assertInSaltComment(
'Python package https://pypi.python.org/packages/source/'
'S/SaltTesting/SaltTesting-0.5.0.tar.gz'
'#md5=e6760af92b7165f8be53b5763e40bc24 is set to be '
'installed',
{'test': ret}
)
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
pip_list = MagicMock(return_value={'SaltTesting': '0.5.0'})
pip_install = MagicMock(return_value={
'retcode': 0,
'stderr': '',
'stdout': 'Downloading/unpacking https://pypi.python.org/packages'
'/source/S/SaltTesting/SaltTesting-0.5.0.tar.gz\n '
'Downloading SaltTesting-0.5.0.tar.gz\n Running '
'setup.py egg_info for package from '
'https://pypi.python.org/packages/source/S/SaltTesting/'
'SaltTesting-0.5.0.tar.gz\n \nCleaning up...'
})
with patch.dict(pip_state.__salt__, {'cmd.run_all': mock,
'pip.list': pip_list,
'pip.install': pip_install}):
ret = pip_state.installed( ret = pip_state.installed(
'https://pypi.python.org/packages/source/S/SaltTesting/' 'https://pypi.python.org/packages/source/S/SaltTesting/'
'SaltTesting-0.5.0.tar.gz' 'SaltTesting-0.5.0.tar.gz'
'#md5=e6760af92b7165f8be53b5763e40bc24' '#md5=e6760af92b7165f8be53b5763e40bc24'
) )
self.assertSaltNoneReturn({'test': ret}) self.assertSaltTrueReturn({'test': ret})
self.assertInSaltComment( self.assertInSaltComment('All packages were successfully installed',
'Python package https://pypi.python.org/packages/source/'
'S/SaltTesting/SaltTesting-0.5.0.tar.gz'
'#md5=e6760af92b7165f8be53b5763e40bc24 is set to be '
'installed',
{'test': ret} {'test': ret}
) )
self.assertInSaltReturn(
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) 'Installed',
pip_list = MagicMock(return_value={'SaltTesting': '0.5.0'}) {'test': ret},
pip_install = MagicMock(return_value={ ('changes', 'https://pypi.python.org/packages/source/S/'
'retcode': 0, 'SaltTesting/SaltTesting-0.5.0.tar.gz'
'stderr': '', '#md5=e6760af92b7165f8be53b5763e40bc24==???')
'stdout': 'Downloading/unpacking https://pypi.python.org/packages'
'/source/S/SaltTesting/SaltTesting-0.5.0.tar.gz\n '
'Downloading SaltTesting-0.5.0.tar.gz\n Running '
'setup.py egg_info for package from '
'https://pypi.python.org/packages/source/S/SaltTesting/'
'SaltTesting-0.5.0.tar.gz\n \nCleaning up...'
})
with patch.dict(pip_state.__salt__, {'cmd.run_all': mock,
'pip.list': pip_list,
'pip.install': pip_install}):
ret = pip_state.installed(
'https://pypi.python.org/packages/source/S/SaltTesting/'
'SaltTesting-0.5.0.tar.gz'
'#md5=e6760af92b7165f8be53b5763e40bc24'
)
self.assertSaltTrueReturn({'test': ret})
self.assertInSaltComment('All packages were successfully installed',
{'test': ret}
)
self.assertInSaltReturn(
'Installed',
{'test': ret},
('changes', 'https://pypi.python.org/packages/source/S/'
'SaltTesting/SaltTesting-0.5.0.tar.gz'
'#md5=e6760af92b7165f8be53b5763e40bc24==???')
)
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
pip_list = MagicMock(return_value={'SaltTesting': '0.5.0'})
pip_install = MagicMock(return_value={
'retcode': 0,
'stderr': '',
'stdout': 'Cloned!'
})
with patch.dict(pip_state.__salt__, {'cmd.run_all': mock,
'pip.list': pip_list,
'pip.install': pip_install}):
with patch.dict(pip_state.__opts__, {'test': False}):
ret = pip_state.installed(
'git+https://github.com/saltstack/salt-testing.git#egg=SaltTesting'
)
self.assertSaltTrueReturn({'test': ret})
self.assertInSaltComment(
'packages are already installed',
{'test': ret}
) )
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
pip_list = MagicMock(return_value={'pep8': '1.3.1'})
pip_install = MagicMock(return_value={'retcode': 0})
with patch.dict(pip_state.__salt__, {'cmd.run_all': mock,
'pip.list': pip_list,
'pip.install': pip_install}):
with patch.dict(pip_state.__opts__, {'test': False}):
ret = pip_state.installed(
'arbitrary ID that should be ignored due to requirements specified',
requirements='/tmp/non-existing-requirements.txt'
)
self.assertSaltTrueReturn({'test': ret})
# Test VCS installations using git+git://
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
pip_list = MagicMock(return_value={'SaltTesting': '0.5.0'})
pip_install = MagicMock(return_value={
'retcode': 0,
'stderr': '',
'stdout': 'Cloned!'
})
with patch.dict(pip_state.__salt__, {'cmd.run_all': mock,
'pip.list': pip_list,
'pip.install': pip_install}):
with patch.dict(pip_state.__opts__, {'test': False}):
ret = pip_state.installed(
'git+git://github.com/saltstack/salt-testing.git#egg=SaltTesting'
)
self.assertSaltTrueReturn({'test': ret})
self.assertInSaltComment(
'packages are already installed',
{'test': ret}
)
# Test VCS installations with version info like >= 0.1
with patch.object(pip, '__version__', MagicMock(side_effect=AttributeError(
'Faked missing __version__ attribute'))):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
pip_list = MagicMock(return_value={'SaltTesting': '0.5.0'}) pip_list = MagicMock(return_value={'SaltTesting': '0.5.0'})
pip_install = MagicMock(return_value={ pip_install = MagicMock(return_value={
@ -260,7 +213,7 @@ class PipStateTest(TestCase, SaltReturnAssertsMixin, LoaderModuleMockMixin):
'pip.install': pip_install}): 'pip.install': pip_install}):
with patch.dict(pip_state.__opts__, {'test': False}): with patch.dict(pip_state.__opts__, {'test': False}):
ret = pip_state.installed( ret = pip_state.installed(
'git+https://github.com/saltstack/salt-testing.git#egg=SaltTesting>=0.5.0' 'git+https://github.com/saltstack/salt-testing.git#egg=SaltTesting'
) )
self.assertSaltTrueReturn({'test': ret}) self.assertSaltTrueReturn({'test': ret})
self.assertInSaltComment( self.assertInSaltComment(
@ -268,6 +221,63 @@ class PipStateTest(TestCase, SaltReturnAssertsMixin, LoaderModuleMockMixin):
{'test': ret} {'test': ret}
) )
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
pip_list = MagicMock(return_value={'pep8': '1.3.1'})
pip_install = MagicMock(return_value={'retcode': 0})
with patch.dict(pip_state.__salt__, {'cmd.run_all': mock,
'pip.list': pip_list,
'pip.install': pip_install}):
with patch.dict(pip_state.__opts__, {'test': False}):
ret = pip_state.installed(
'arbitrary ID that should be ignored due to requirements specified',
requirements='/tmp/non-existing-requirements.txt'
)
self.assertSaltTrueReturn({'test': ret})
# Test VCS installations using git+git://
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
pip_list = MagicMock(return_value={'SaltTesting': '0.5.0'})
pip_install = MagicMock(return_value={
'retcode': 0,
'stderr': '',
'stdout': 'Cloned!'
})
with patch.dict(pip_state.__salt__, {'cmd.run_all': mock,
'pip.list': pip_list,
'pip.install': pip_install}):
with patch.dict(pip_state.__opts__, {'test': False}):
ret = pip_state.installed(
'git+git://github.com/saltstack/salt-testing.git#egg=SaltTesting'
)
self.assertSaltTrueReturn({'test': ret})
self.assertInSaltComment(
'packages are already installed',
{'test': ret}
)
# Test VCS installations with version info like >= 0.1
with patch.object(pip, '__version__', MagicMock(side_effect=AttributeError(
'Faked missing __version__ attribute'))):
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
pip_list = MagicMock(return_value={'SaltTesting': '0.5.0'})
pip_install = MagicMock(return_value={
'retcode': 0,
'stderr': '',
'stdout': 'Cloned!'
})
with patch.dict(pip_state.__salt__, {'cmd.run_all': mock,
'pip.list': pip_list,
'pip.install': pip_install}):
with patch.dict(pip_state.__opts__, {'test': False}):
ret = pip_state.installed(
'git+https://github.com/saltstack/salt-testing.git#egg=SaltTesting>=0.5.0'
)
self.assertSaltTrueReturn({'test': ret})
self.assertInSaltComment(
'packages are already installed',
{'test': ret}
)
def test_install_in_editable_mode(self): def test_install_in_editable_mode(self):
''' '''
Check that `name` parameter containing bad characters is not parsed by Check that `name` parameter containing bad characters is not parsed by
@ -281,9 +291,11 @@ class PipStateTest(TestCase, SaltReturnAssertsMixin, LoaderModuleMockMixin):
'stderr': '', 'stderr': '',
'stdout': 'Cloned!' 'stdout': 'Cloned!'
}) })
pip_version = MagicMock(return_value='10.0.1')
with patch.dict(pip_state.__salt__, {'cmd.run_all': mock, with patch.dict(pip_state.__salt__, {'cmd.run_all': mock,
'pip.list': pip_list, 'pip.list': pip_list,
'pip.install': pip_install}): 'pip.install': pip_install,
'pip.version': pip_version}):
ret = pip_state.installed('state@name', ret = pip_state.installed('state@name',
cwd='/path/to/project', cwd='/path/to/project',
editable=['.']) editable=['.'])

View File

@ -19,6 +19,7 @@ integration.modules.test_mine
integration.modules.test_network integration.modules.test_network
integration.modules.test_ntp integration.modules.test_ntp
integration.modules.test_pillar integration.modules.test_pillar
integration.modules.test_pip
integration.modules.test_pkg integration.modules.test_pkg
integration.modules.test_publish integration.modules.test_publish
integration.modules.test_service integration.modules.test_service
@ -36,6 +37,7 @@ integration.runners.test_jobs
integration.runners.test_salt integration.runners.test_salt
integration.sdb.test_env integration.sdb.test_env
integration.states.test_host integration.states.test_host
integration.states.test_pip
integration.states.test_renderers integration.states.test_renderers
integration.utils.testprogram integration.utils.testprogram
integration.wheel.test_client integration.wheel.test_client