Merge pull request #25561 from twangboy/fix_21041_again

Fixed the gem module to work on windows... without injection
This commit is contained in:
Mike Place 2015-07-20 15:12:15 -06:00
commit 3c32b0b669
2 changed files with 135 additions and 42 deletions

View file

@ -4,15 +4,41 @@ Manage ruby gems.
'''
from __future__ import absolute_import
try:
from shlex import quote as _cmd_quote # pylint: disable=E0611
except ImportError:
from pipes import quote as _cmd_quote
# Import python libs
import re
# Import salt libs
import salt.utils
__func_alias__ = {
'list_': 'list'
}
def _gem(command, ruby=None, runas=None, gem_bin=None):
'''
Run the actual gem command. If rvm or rbenv is installed, run the command
using the corresponding module. rbenv is not available on windows, so don't
try.
:param command: string
Command to run
:param ruby: string : None
If RVM or rbenv are installed, the ruby version and gemset to use.
Ignored if ``gem_bin`` is specified.
:param runas: string : None
The user to run gem as.
:param gem_bin: string : None
Full path to the ``gem`` binary
:return:
Returns the full standard out including success codes or False if it fails
'''
cmdline = '{gem} {command}'.format(gem=gem_bin or 'gem', command=command)
# If a custom gem is given, use that and don't check for rvm/rbenv. User
@ -21,7 +47,7 @@ def _gem(command, ruby=None, runas=None, gem_bin=None):
if __salt__['rvm.is_installed'](runas=runas):
return __salt__['rvm.do'](ruby, cmdline, runas=runas)
if __salt__['rbenv.is_installed'](runas=runas):
if not salt.utils.is_windows() and __salt__['rbenv.is_installed'](runas=runas):
if ruby is None:
return __salt__['rbenv.do'](cmdline, runas=runas)
else:
@ -30,7 +56,7 @@ def _gem(command, ruby=None, runas=None, gem_bin=None):
ret = __salt__['cmd.run_all'](
cmdline,
runas=runas,
python_shell=False
python_shell=True
)
if ret['retcode'] == 0:
@ -51,25 +77,25 @@ def install(gems, # pylint: disable=C0103
'''
Installs one or several gems.
gems
:param gems: string
The gems to install
gem_bin : None
:param gem_bin: string : None
Full path to ``gem`` binary to use.
ruby : None
:param ruby: string : None
If RVM or rbenv are installed, the ruby version and gemset to use.
Ignored if ``gem_bin`` is specified.
runas : None
:param runas: string : None
The user to run gem as.
version : None
:param version: string : None
Specify the version to install for the gem.
Doesn't play nice with multiple gems at once
rdoc : False
:param rdoc: boolean : False
Generate RDoc documentation for the gem(s).
ri : False
:param ri: boolean : False
Generate RI documentation for the gem(s).
pre_releases
:param pre_releases: boolean : False
Include pre-releases in the available versions
proxy : None
:param proxy: string : None
Use the specified HTTP proxy server for all outgoing traffic.
Format: http://hostname[:port]
@ -81,8 +107,18 @@ def install(gems, # pylint: disable=C0103
salt '*' gem.install redphone gem_bin=/opt/sensu/embedded/bin/gem
'''
# Check for injection
if gems:
gems = ' '.join([_cmd_quote(gem) for gem in gems.split()])
if ruby:
ruby = _cmd_quote(ruby)
if gem_bin:
gem_bin = _cmd_quote(gem_bin)
options = []
if version:
version = _cmd_quote(version) # injection check
options.append('--version {0}'.format(version))
if not rdoc:
options.append('--no-rdoc')
@ -91,6 +127,7 @@ def install(gems, # pylint: disable=C0103
if pre_releases:
options.append('--pre')
if proxy:
proxy = _cmd_quote(proxy) # injection check
options.append('-p {0}'.format(proxy))
cmdline_args = ' '.join(options)
@ -105,14 +142,14 @@ def uninstall(gems, ruby=None, runas=None, gem_bin=None):
'''
Uninstall one or several gems.
gems
:param gems: string
The gems to uninstall.
gem_bin : None
:param gem_bin: string : None
Full path to ``gem`` binary to use.
ruby : None
:param ruby: string : None
If RVM or rbenv are installed, the ruby version and gemset to use.
Ignored if ``gem_bin`` is specified.
runas : None
:param runas: string : None
The user to run gem as.
CLI Example:
@ -121,7 +158,15 @@ def uninstall(gems, ruby=None, runas=None, gem_bin=None):
salt '*' gem.uninstall vagrant
'''
return _gem('uninstall {gems}'.format(gems=gems),
# Check for injection
if gems:
gems = ' '.join([_cmd_quote(gem) for gem in gems.split()])
if ruby:
ruby = _cmd_quote(ruby)
if gem_bin:
gem_bin = _cmd_quote(gem_bin)
return _gem('uninstall {gems} -a -x'.format(gems=gems),
ruby,
gem_bin=gem_bin,
runas=runas)
@ -131,14 +176,14 @@ def update(gems, ruby=None, runas=None, gem_bin=None):
'''
Update one or several gems.
gems
:param gems: string
The gems to update.
gem_bin : None
:param gem_bin: string : None
Full path to ``gem`` binary to use.
ruby : None
:param ruby: string : None
If RVM or rbenv are installed, the ruby version and gemset to use.
Ignored if ``gem_bin`` is specified.
runas : None
:param runas: string : None
The user to run gem as.
CLI Example:
@ -147,6 +192,14 @@ def update(gems, ruby=None, runas=None, gem_bin=None):
salt '*' gem.update vagrant
'''
# Check for injection
if gems:
gems = ' '.join([_cmd_quote(gem) for gem in gems.split()])
if ruby:
ruby = _cmd_quote(ruby)
if gem_bin:
gem_bin = _cmd_quote(gem_bin)
return _gem('update {gems}'.format(gems=gems),
ruby,
gem_bin=gem_bin,
@ -157,14 +210,14 @@ def update_system(version='', ruby=None, runas=None, gem_bin=None):
'''
Update rubygems.
version : (newest)
:param version: string : (newest)
The version of rubygems to install.
gem_bin : None
:param gem_bin: string : None
Full path to ``gem`` binary to use.
ruby : None
:param ruby: string : None
If RVM or rbenv are installed, the ruby version and gemset to use.
Ignored if ``gem_bin`` is specified.
runas : None
:param runas: string : None
The user to run gem as.
CLI Example:
@ -173,6 +226,14 @@ def update_system(version='', ruby=None, runas=None, gem_bin=None):
salt '*' gem.update_system
'''
# Check for injection
if version:
version = _cmd_quote(version)
if ruby:
ruby = _cmd_quote(ruby)
if gem_bin:
gem_bin = _cmd_quote(gem_bin)
return _gem('update --system {version}'.format(version=version),
ruby,
gem_bin=gem_bin,
@ -183,14 +244,14 @@ def list_(prefix='', ruby=None, runas=None, gem_bin=None):
'''
List locally installed gems.
prefix :
:param prefix: string :
Only list gems when the name matches this prefix.
gem_bin : None
:param gem_bin: string : None
Full path to ``gem`` binary to use.
ruby : None
:param ruby: string : None
If RVM or rbenv are installed, the ruby version and gemset to use.
Ignored if ``gem_bin`` is specified.
runas : None
:param runas: string : None
The user to run gem as.
CLI Example:
@ -200,10 +261,19 @@ def list_(prefix='', ruby=None, runas=None, gem_bin=None):
salt '*' gem.list
'''
gems = {}
# Check for injection
if prefix:
prefix = _cmd_quote(prefix)
if ruby:
ruby = _cmd_quote(ruby)
if gem_bin:
gem_bin = _cmd_quote(gem_bin)
stdout = _gem('list {prefix}'.format(prefix=prefix),
ruby,
gem_bin=gem_bin,
runas=runas)
lines = []
if isinstance(stdout, str):
lines = stdout.splitlines()
@ -213,6 +283,7 @@ def list_(prefix='', ruby=None, runas=None, gem_bin=None):
gem = match.group(1)
versions = match.group(2).split(', ')
gems[gem] = versions
return gems
@ -220,14 +291,14 @@ def sources_add(source_uri, ruby=None, runas=None, gem_bin=None):
'''
Add a gem source.
source_uri
:param source_uri: string
The source URI to add.
gem_bin : None
:param gem_bin: string : None
Full path to ``gem`` binary to use.
ruby : None
:param ruby: string : None
If RVM or rbenv are installed, the ruby version and gemset to use.
Ignored if ``gem_bin`` is specified.
runas : None
:param runas: string : None
The user to run gem as.
CLI Example:
@ -236,6 +307,14 @@ def sources_add(source_uri, ruby=None, runas=None, gem_bin=None):
salt '*' gem.sources_add http://rubygems.org/
'''
# Check for injection
if source_uri:
source_uri = _cmd_quote(source_uri)
if ruby:
ruby = _cmd_quote(ruby)
if gem_bin:
gem_bin = _cmd_quote(gem_bin)
return _gem('sources --add {source_uri}'.format(source_uri=source_uri),
ruby,
gem_bin=gem_bin,
@ -246,14 +325,14 @@ def sources_remove(source_uri, ruby=None, runas=None, gem_bin=None):
'''
Remove a gem source.
source_uri
:param source_uri: string
The source URI to remove.
gem_bin : None
:param gem_bin: string : None
Full path to ``gem`` binary to use.
ruby : None
:param ruby: string : None
If RVM or rbenv are installed, the ruby version and gemset to use.
Ignored if ``gem_bin`` is specified.
runas : None
:param runas: string : None
The user to run gem as.
CLI Example:
@ -262,6 +341,14 @@ def sources_remove(source_uri, ruby=None, runas=None, gem_bin=None):
salt '*' gem.sources_remove http://rubygems.org/
'''
# Check for injection
if source_uri:
source_uri = _cmd_quote(source_uri)
if ruby:
ruby = _cmd_quote(ruby)
if gem_bin:
gem_bin = _cmd_quote(gem_bin)
return _gem('sources --remove {source_uri}'.format(source_uri=source_uri),
ruby,
gem_bin=gem_bin,
@ -272,12 +359,12 @@ def sources_list(ruby=None, runas=None, gem_bin=None):
'''
List the configured gem sources.
gem_bin : None
:param gem_bin: string : None
Full path to ``gem`` binary to use.
ruby : None
:param ruby: string : None
If RVM or rbenv are installed, the ruby version and gemset to use.
Ignored if ``gem_bin`` is specified.
runas : None
:param runas: string : None
The user to run gem as.
CLI Example:
@ -286,5 +373,11 @@ def sources_list(ruby=None, runas=None, gem_bin=None):
salt '*' gem.sources_list
'''
# Check for injection
if ruby:
ruby = _cmd_quote(ruby)
if gem_bin:
gem_bin = _cmd_quote(gem_bin)
ret = _gem('sources', ruby, gem_bin=gem_bin, runas=runas)
return [] if ret is False else ret.splitlines()[2:]

View file

@ -22,7 +22,7 @@ class TestGemModule(TestCase):
'rbenv.is_installed': MagicMock(return_value=False),
'cmd.run_all': mock}):
gem._gem('install rails')
mock.assert_called_once_with('gem install rails', runas=None, python_shell=False)
mock.assert_called_once_with('gem install rails', runas=None, python_shell=True)
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
rvm_mock = MagicMock()
@ -32,7 +32,7 @@ class TestGemModule(TestCase):
'cmd.run_all': mock}):
gem._gem('install rails', gem_bin="/usr/local/bin/gem")
self.assertEqual(False, rvm_mock.called, "Should never call rvm.is_installed if gem_bin provided")
mock.assert_called_once_with('/usr/local/bin/gem install rails', runas=None, python_shell=False)
mock.assert_called_once_with('/usr/local/bin/gem install rails', runas=None, python_shell=True)
mock = MagicMock(return_value=None)
with patch.dict(gem.__salt__,
@ -62,7 +62,7 @@ class TestGemModule(TestCase):
'cmd.run_all': mock}):
gem.install('rails', pre_releases=True)
mock.assert_called_once_with(
'gem install rails --no-rdoc --no-ri --pre', runas=None, python_shell=False
'gem install rails --no-rdoc --no-ri --pre', runas=None, python_shell=True
)
def test_list(self):