mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge pull request #44533 from cloudflare/nested-colors
Nested outputter colors depending on the function retcode
This commit is contained in:
commit
81ded2aaba
4 changed files with 166 additions and 15 deletions
|
@ -145,8 +145,10 @@ class BaseCaller(object):
|
|||
print_ret = ret.get('return', {})
|
||||
salt.output.display_output(
|
||||
{'local': print_ret},
|
||||
out,
|
||||
self.opts)
|
||||
out=out,
|
||||
opts=self.opts,
|
||||
_retcode=ret.get('retcode', 0))
|
||||
# _retcode will be available in the kwargs of the outputter function
|
||||
if self.opts.get('retcode_passthrough', False):
|
||||
sys.exit(ret['retcode'])
|
||||
except SaltInvocationError as err:
|
||||
|
@ -372,8 +374,10 @@ class RAETCaller(BaseCaller):
|
|||
self.process.terminate()
|
||||
salt.output.display_output(
|
||||
{'local': print_ret},
|
||||
ret.get('out', 'nested'),
|
||||
self.opts)
|
||||
out=ret.get('out', 'nested'),
|
||||
opts=self.opts,
|
||||
_retcode=ret.get('retcode', 0))
|
||||
# _retcode will be available in the kwargs of the outputter function
|
||||
if self.opts.get('retcode_passthrough', False):
|
||||
sys.exit(ret['retcode'])
|
||||
|
||||
|
|
|
@ -181,7 +181,7 @@ class SaltCMD(salt.utils.parsers.SaltCMDOptionParser):
|
|||
for full_ret in self.local_client.cmd_cli(**kwargs):
|
||||
ret_, out, retcode = self._format_ret(full_ret)
|
||||
ret.update(ret_)
|
||||
self._output_ret(ret, out)
|
||||
self._output_ret(ret, out, retcode=retcode)
|
||||
else:
|
||||
if self.options.verbose:
|
||||
kwargs['verbose'] = True
|
||||
|
@ -190,7 +190,7 @@ class SaltCMD(salt.utils.parsers.SaltCMDOptionParser):
|
|||
try:
|
||||
ret_, out, retcode = self._format_ret(full_ret)
|
||||
retcodes.append(retcode)
|
||||
self._output_ret(ret_, out)
|
||||
self._output_ret(ret_, out, retcode=retcode)
|
||||
ret.update(full_ret)
|
||||
except KeyError:
|
||||
errors.append(full_ret)
|
||||
|
@ -212,7 +212,7 @@ class SaltCMD(salt.utils.parsers.SaltCMDOptionParser):
|
|||
|
||||
except (SaltInvocationError, EauthAuthenticationError, SaltClientError) as exc:
|
||||
ret = str(exc)
|
||||
self._output_ret(ret, '')
|
||||
self._output_ret(ret, '', retcode=1)
|
||||
|
||||
def _preview_target(self):
|
||||
'''
|
||||
|
@ -352,7 +352,7 @@ class SaltCMD(salt.utils.parsers.SaltCMDOptionParser):
|
|||
'Requested job was still run but output cannot be displayed.\n')
|
||||
salt.output.update_progress(self.config, progress, self.progress_bar, out)
|
||||
|
||||
def _output_ret(self, ret, out):
|
||||
def _output_ret(self, ret, out, retcode=0):
|
||||
'''
|
||||
Print the output from a single return to the terminal
|
||||
'''
|
||||
|
@ -362,7 +362,10 @@ class SaltCMD(salt.utils.parsers.SaltCMDOptionParser):
|
|||
self._print_docs(ret)
|
||||
else:
|
||||
# Determine the proper output method and run it
|
||||
salt.output.display_output(ret, out, self.config)
|
||||
salt.output.display_output(ret,
|
||||
out=out,
|
||||
opts=self.config,
|
||||
_retcode=retcode)
|
||||
if not ret:
|
||||
sys.stderr.write('ERROR: No return received\n')
|
||||
sys.exit(2)
|
||||
|
|
|
@ -39,7 +39,7 @@ class NestDisplay(object):
|
|||
'''
|
||||
Manage the nested display contents
|
||||
'''
|
||||
def __init__(self):
|
||||
def __init__(self, retcode=0):
|
||||
self.__dict__.update(
|
||||
salt.utils.color.get_colors(
|
||||
__opts__.get('color'),
|
||||
|
@ -47,6 +47,7 @@ class NestDisplay(object):
|
|||
)
|
||||
)
|
||||
self.strip_colors = __opts__.get('strip_colors', True)
|
||||
self.retcode = retcode
|
||||
|
||||
def ustring(self,
|
||||
indent,
|
||||
|
@ -109,12 +110,15 @@ class NestDisplay(object):
|
|||
)
|
||||
first_line = False
|
||||
elif isinstance(ret, (list, tuple)):
|
||||
color = self.GREEN
|
||||
if self.retcode != 0:
|
||||
color = self.RED
|
||||
for ind in ret:
|
||||
if isinstance(ind, (list, tuple, dict)):
|
||||
out.append(
|
||||
self.ustring(
|
||||
indent,
|
||||
self.GREEN,
|
||||
color,
|
||||
'|_'
|
||||
)
|
||||
)
|
||||
|
@ -124,10 +128,13 @@ class NestDisplay(object):
|
|||
self.display(ind, indent, '- ', out)
|
||||
elif isinstance(ret, dict):
|
||||
if indent:
|
||||
color = self.CYAN
|
||||
if self.retcode != 0:
|
||||
color = self.RED
|
||||
out.append(
|
||||
self.ustring(
|
||||
indent,
|
||||
self.CYAN,
|
||||
color,
|
||||
'----------'
|
||||
)
|
||||
)
|
||||
|
@ -137,13 +144,15 @@ class NestDisplay(object):
|
|||
keys = ret.keys()
|
||||
else:
|
||||
keys = sorted(ret)
|
||||
|
||||
color = self.CYAN
|
||||
if self.retcode != 0:
|
||||
color = self.RED
|
||||
for key in keys:
|
||||
val = ret[key]
|
||||
out.append(
|
||||
self.ustring(
|
||||
indent,
|
||||
self.CYAN,
|
||||
color,
|
||||
key,
|
||||
suffix=':',
|
||||
prefix=prefix
|
||||
|
@ -158,7 +167,8 @@ def output(ret, **kwargs):
|
|||
Display ret data
|
||||
'''
|
||||
# Prefer kwargs before opts
|
||||
retcode = kwargs.get('_retcode', 0)
|
||||
base_indent = kwargs.get('nested_indent', 0) \
|
||||
or __opts__.get('nested_indent', 0)
|
||||
nest = NestDisplay()
|
||||
nest = NestDisplay(retcode=retcode)
|
||||
return '\n'.join(nest.display(ret, base_indent, '', []))
|
||||
|
|
134
tests/unit/output/test_nested_out.py
Normal file
134
tests/unit/output/test_nested_out.py
Normal file
|
@ -0,0 +1,134 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Unit tests for the Nested outputter
|
||||
'''
|
||||
|
||||
# Import Python Libs
|
||||
from __future__ import absolute_import
|
||||
|
||||
# Import Salt Testing Libs
|
||||
from tests.support.mixins import LoaderModuleMockMixin
|
||||
from tests.support.unit import TestCase
|
||||
|
||||
# Import Salt Libs
|
||||
import salt.output.nested as nested
|
||||
|
||||
|
||||
class NestedOutputterTestCase(TestCase, LoaderModuleMockMixin):
|
||||
'''
|
||||
Test cases for salt.output.nested
|
||||
'''
|
||||
def setup_loader_modules(self):
|
||||
return {
|
||||
nested: {
|
||||
'__opts__': {
|
||||
'extension_modules': '',
|
||||
'color': True
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
# The example from the documentation for the test.arg execution function
|
||||
# Same function from the highstate outputter
|
||||
self.data = {
|
||||
'local': {
|
||||
'args': (1, 'two', 3.1),
|
||||
'kwargs': {
|
||||
u'__pub_pid': 25938,
|
||||
'wow': {
|
||||
'a': 1,
|
||||
'b': 'hello'
|
||||
},
|
||||
u'__pub_fun': 'test.arg',
|
||||
u'__pub_jid': '20171207105927331329',
|
||||
u'__pub_tgt': 'salt-call',
|
||||
'txt': 'hello'
|
||||
}
|
||||
}
|
||||
}
|
||||
self.addCleanup(delattr, self, 'data')
|
||||
|
||||
def test_output_with_colors(self):
|
||||
# Should look exacly like that, with the default color scheme:
|
||||
#
|
||||
# local:
|
||||
# ----------
|
||||
# args:
|
||||
# - 1
|
||||
# - two
|
||||
# - 3.1
|
||||
# kwargs:
|
||||
# ----------
|
||||
# __pub_fun:
|
||||
# test.arg
|
||||
# __pub_jid:
|
||||
# 20171207105927331329
|
||||
# __pub_pid:
|
||||
# 25938
|
||||
# __pub_tgt:
|
||||
# salt-call
|
||||
# txt:
|
||||
# hello
|
||||
# wow:
|
||||
# ----------
|
||||
# a:
|
||||
# 1
|
||||
# b:
|
||||
# hello
|
||||
expected_output_str = (
|
||||
'\x1b[0;36mlocal\x1b[0;0m:\n \x1b[0;36m----------\x1b[0;0m\n \x1b[0;36margs\x1b[0;0m:\n'
|
||||
' \x1b[0;1;33m- 1\x1b[0;0m\n \x1b[0;32m- two\x1b[0;0m\n \x1b[0;1;33m- 3.1\x1b[0;0m\n'
|
||||
' \x1b[0;36mkwargs\x1b[0;0m:\n \x1b[0;36m----------\x1b[0;0m\n'
|
||||
' \x1b[0;36m__pub_fun\x1b[0;0m:\n \x1b[0;32mtest.arg\x1b[0;0m\n'
|
||||
' \x1b[0;36m__pub_jid\x1b[0;0m:\n \x1b[0;32m20171207105927331329\x1b[0;0m\n'
|
||||
' \x1b[0;36m__pub_pid\x1b[0;0m:\n \x1b[0;1;33m25938\x1b[0;0m\n'
|
||||
' \x1b[0;36m__pub_tgt\x1b[0;0m:\n \x1b[0;32msalt-call\x1b[0;0m\n'
|
||||
' \x1b[0;36mtxt\x1b[0;0m:\n \x1b[0;32mhello\x1b[0;0m\n \x1b[0;36mwow\x1b[0;0m:\n'
|
||||
' \x1b[0;36m----------\x1b[0;0m\n \x1b[0;36ma\x1b[0;0m:\n'
|
||||
' \x1b[0;1;33m1\x1b[0;0m\n \x1b[0;36mb\x1b[0;0m:\n'
|
||||
' \x1b[0;32mhello\x1b[0;0m'
|
||||
)
|
||||
ret = nested.output(self.data)
|
||||
self.assertEqual(ret, expected_output_str)
|
||||
|
||||
def test_output_with_retcode(self):
|
||||
# Non-zero retcode should change the colors
|
||||
# Same output format as above, just different colors
|
||||
expected_output_str = (
|
||||
'\x1b[0;31mlocal\x1b[0;0m:\n \x1b[0;31m----------\x1b[0;0m\n \x1b[0;31margs\x1b[0;0m:\n'
|
||||
' \x1b[0;1;33m- 1\x1b[0;0m\n \x1b[0;32m- two\x1b[0;0m\n \x1b[0;1;33m- 3.1\x1b[0;0m\n'
|
||||
' \x1b[0;31mkwargs\x1b[0;0m:\n \x1b[0;31m----------\x1b[0;0m\n'
|
||||
' \x1b[0;31m__pub_fun\x1b[0;0m:\n \x1b[0;32mtest.arg\x1b[0;0m\n'
|
||||
' \x1b[0;31m__pub_jid\x1b[0;0m:\n \x1b[0;32m20171207105927331329\x1b[0;0m\n'
|
||||
' \x1b[0;31m__pub_pid\x1b[0;0m:\n \x1b[0;1;33m25938\x1b[0;0m\n'
|
||||
' \x1b[0;31m__pub_tgt\x1b[0;0m:\n \x1b[0;32msalt-call\x1b[0;0m\n'
|
||||
' \x1b[0;31mtxt\x1b[0;0m:\n \x1b[0;32mhello\x1b[0;0m\n \x1b[0;31mwow\x1b[0;0m:\n'
|
||||
' \x1b[0;31m----------\x1b[0;0m\n \x1b[0;31ma\x1b[0;0m:\n'
|
||||
' \x1b[0;1;33m1\x1b[0;0m\n \x1b[0;31mb\x1b[0;0m:\n'
|
||||
' \x1b[0;32mhello\x1b[0;0m'
|
||||
)
|
||||
# You can notice that in test_output_with_colors the color code is \x1b[0;36m, i.e., GREEN,
|
||||
# while here the color code is \x1b[0;31m, i.e., RED (failure)
|
||||
ret = nested.output(self.data, _retcode=1)
|
||||
self.assertEqual(ret, expected_output_str)
|
||||
|
||||
def test_output_with_indent(self):
|
||||
# Everything must be indented by exactly two spaces
|
||||
# (using nested_indent=2 sent to nested.output as kwarg)
|
||||
expected_output_str = (
|
||||
' \x1b[0;36m----------\x1b[0;0m\n \x1b[0;36mlocal\x1b[0;0m:\n \x1b[0;36m----------\x1b[0;0m\n'
|
||||
' \x1b[0;36margs\x1b[0;0m:\n \x1b[0;1;33m- 1\x1b[0;0m\n \x1b[0;32m- two\x1b[0;0m\n'
|
||||
' \x1b[0;1;33m- 3.1\x1b[0;0m\n \x1b[0;36mkwargs\x1b[0;0m:\n'
|
||||
' \x1b[0;36m----------\x1b[0;0m\n \x1b[0;36m__pub_fun\x1b[0;0m:\n'
|
||||
' \x1b[0;32mtest.arg\x1b[0;0m\n \x1b[0;36m__pub_jid\x1b[0;0m:\n'
|
||||
' \x1b[0;32m20171207105927331329\x1b[0;0m\n \x1b[0;36m__pub_pid\x1b[0;0m:\n'
|
||||
' \x1b[0;1;33m25938\x1b[0;0m\n \x1b[0;36m__pub_tgt\x1b[0;0m:\n'
|
||||
' \x1b[0;32msalt-call\x1b[0;0m\n \x1b[0;36mtxt\x1b[0;0m:\n'
|
||||
' \x1b[0;32mhello\x1b[0;0m\n \x1b[0;36mwow\x1b[0;0m:\n'
|
||||
' \x1b[0;36m----------\x1b[0;0m\n \x1b[0;36ma\x1b[0;0m:\n'
|
||||
' \x1b[0;1;33m1\x1b[0;0m\n \x1b[0;36mb\x1b[0;0m:\n'
|
||||
' \x1b[0;32mhello\x1b[0;0m'
|
||||
)
|
||||
ret = nested.output(self.data, nested_indent=2)
|
||||
self.assertEqual(ret, expected_output_str)
|
Loading…
Add table
Reference in a new issue