mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Improve handling of npm json output (#43138)
Rather than look for [ or { at the beginning of a line to identify the json output, just use salt.utils(.json).findjson.
This commit is contained in:
parent
9874429741
commit
7ff3c9c5ff
2 changed files with 66 additions and 41 deletions
|
@ -160,7 +160,11 @@ def install(pkg=None,
|
|||
env.update({'SUDO_UID': uid, 'SUDO_USER': ''})
|
||||
|
||||
cmd = ' '.join(cmd)
|
||||
result = __salt__['cmd.run_all'](cmd, python_shell=True, cwd=dir, runas=runas, env=env)
|
||||
result = __salt__['cmd.run_all'](cmd,
|
||||
python_shell=True,
|
||||
cwd=dir,
|
||||
runas=runas,
|
||||
env=env)
|
||||
|
||||
if result['retcode'] != 0:
|
||||
raise CommandExecutionError(result['stderr'])
|
||||
|
@ -168,33 +172,9 @@ def install(pkg=None,
|
|||
# npm >1.2.21 is putting the output to stderr even though retcode is 0
|
||||
npm_output = result['stdout'] or result['stderr']
|
||||
try:
|
||||
return json.loads(npm_output)
|
||||
return salt.utils.find_json(npm_output)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
json_npm_output = _extract_json(npm_output)
|
||||
return json_npm_output or npm_output
|
||||
|
||||
|
||||
def _extract_json(npm_output):
|
||||
lines = npm_output.splitlines()
|
||||
log.error(lines)
|
||||
|
||||
# Strip all lines until JSON output starts
|
||||
while lines and not lines[0].startswith('{') and not lines[0].startswith('['):
|
||||
lines = lines[1:]
|
||||
while lines and not lines[-1].startswith('}') and not lines[-1].startswith(']'):
|
||||
lines = lines[:-1]
|
||||
# macOS with fsevents includes the following line in the return
|
||||
# when a new module is installed which is invalid JSON:
|
||||
# [fsevents] Success: "..."
|
||||
while lines and (lines[0].startswith('[fsevents]') or lines[0].startswith('Pass ')):
|
||||
lines = lines[1:]
|
||||
try:
|
||||
return json.loads(''.join(lines))
|
||||
except ValueError:
|
||||
pass
|
||||
return None
|
||||
return npm_output
|
||||
|
||||
|
||||
def uninstall(pkg, dir=None, runas=None, env=None):
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
# Import Python Libs
|
||||
from __future__ import absolute_import
|
||||
import json
|
||||
import textwrap
|
||||
|
||||
# Import Salt Testing Libs
|
||||
from tests.support.mixins import LoaderModuleMockMixin
|
||||
|
@ -34,43 +35,87 @@ class NpmTestCase(TestCase, LoaderModuleMockMixin):
|
|||
self.addCleanup(patcher.stop)
|
||||
return {npm: {}}
|
||||
|
||||
# 'install' function tests: 1
|
||||
# 'install' function tests: 4
|
||||
|
||||
def test_install(self):
|
||||
'''
|
||||
Test if it install an NPM package.
|
||||
Test if it installs an NPM package.
|
||||
'''
|
||||
mock = MagicMock(return_value={'retcode': 1, 'stderr': 'error'})
|
||||
with patch.dict(npm.__salt__, {'cmd.run_all': mock}):
|
||||
self.assertRaises(CommandExecutionError, npm.install,
|
||||
'coffee-script')
|
||||
|
||||
mock = MagicMock(return_value={'retcode': 0, 'stderr': 'error',
|
||||
'stdout': '{"salt": ["SALT"]}'})
|
||||
with patch.dict(npm.__salt__, {'cmd.run_all': mock}):
|
||||
mock_err = MagicMock(return_value='SALT')
|
||||
with patch.object(json, 'loads', mock_err):
|
||||
self.assertEqual(npm.install('coffee-script'), 'SALT')
|
||||
# This is at least somewhat closer to the actual output format.
|
||||
mock_json_out = textwrap.dedent('''\
|
||||
[
|
||||
{
|
||||
"salt": "SALT"
|
||||
}
|
||||
]''')
|
||||
|
||||
mock = MagicMock(return_value={'retcode': 0, 'stderr': 'error',
|
||||
'stdout': '{"salt": ["SALT"]}'})
|
||||
# Successful run, expected output format
|
||||
mock = MagicMock(return_value={'retcode': 0, 'stderr': '',
|
||||
'stdout': mock_json_out})
|
||||
with patch.dict(npm.__salt__, {'cmd.run_all': mock}):
|
||||
self.assertEqual(npm.install('coffee-script'),
|
||||
[{u'salt': u'SALT'}])
|
||||
|
||||
mock_json_out_extra = textwrap.dedent('''\
|
||||
Compilation output here
|
||||
|
||||
[bcrypt] Success: "/tmp/node_modules/bcrypt/foo" is installed via remote"
|
||||
[grpc] Success: "/usr/lib/node_modules/@foo/bar" is installed via remote"
|
||||
[
|
||||
{
|
||||
"from" : "express@",
|
||||
"name" : "express",
|
||||
"dependencies" : {
|
||||
"escape-html" : {
|
||||
"from" : "escape-html@~1.0.3",
|
||||
"dependencies" : {},
|
||||
"version" : "1.0.3"
|
||||
}
|
||||
},
|
||||
"version" : "4.16.3"
|
||||
}
|
||||
]''')
|
||||
extra_expected = [{u'dependencies':
|
||||
{u'escape-html': {
|
||||
u'dependencies': {},
|
||||
u'from': u'escape-html@~1.0.3',
|
||||
u'version': u'1.0.3'}
|
||||
},
|
||||
u'from': u'express@',
|
||||
u'name': u'express',
|
||||
u'version': u'4.16.3'}]
|
||||
|
||||
# Successful run, expected output format with additional leading text
|
||||
mock = MagicMock(return_value={'retcode': 0, 'stderr': '',
|
||||
'stdout': mock_json_out_extra})
|
||||
with patch.dict(npm.__salt__, {'cmd.run_all': mock}):
|
||||
self.assertEqual(npm.install('coffee-script'), extra_expected)
|
||||
|
||||
# Successful run, unexpected output format
|
||||
mock = MagicMock(return_value={'retcode': 0, 'stderr': '',
|
||||
'stdout': 'SALT'})
|
||||
with patch.dict(npm.__salt__, {'cmd.run_all': mock}):
|
||||
mock_err = MagicMock(side_effect=ValueError())
|
||||
# When JSON isn't successfully parsed, return should equal input
|
||||
with patch.object(json, 'loads', mock_err):
|
||||
self.assertEqual(npm.install('coffee-script'),
|
||||
'{"salt": ["SALT"]}')
|
||||
self.assertEqual(npm.install('coffee-script'), 'SALT')
|
||||
|
||||
# 'uninstall' function tests: 1
|
||||
|
||||
def test_uninstall(self):
|
||||
'''
|
||||
Test if it uninstall an NPM package.
|
||||
Test if it uninstalls an NPM package.
|
||||
'''
|
||||
mock = MagicMock(return_value={'retcode': 1, 'stderr': 'error'})
|
||||
with patch.dict(npm.__salt__, {'cmd.run_all': mock}):
|
||||
self.assertFalse(npm.uninstall('coffee-script'))
|
||||
|
||||
mock = MagicMock(return_value={'retcode': 0, 'stderr': 'error'})
|
||||
mock = MagicMock(return_value={'retcode': 0, 'stderr': ''})
|
||||
with patch.dict(npm.__salt__, {'cmd.run_all': mock}):
|
||||
self.assertTrue(npm.uninstall('coffee-script'))
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue