Skip to content

Commit

Permalink
Fix salt-ssh stacktrace when retcode is not an integer
Browse files Browse the repository at this point in the history
  • Loading branch information
lkubb committed Jun 29, 2023
1 parent 8869972 commit 4ee3ef4
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 2 deletions.
1 change: 1 addition & 0 deletions changelog/64575.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed salt-ssh stacktrace when retcode is not an integer
12 changes: 11 additions & 1 deletion salt/client/ssh/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,14 @@ def handle_routine(self, que, opts, host, target, mine=False):
ret["ret"] = data["local"]
try:
# Ensure a reported local retcode is kept
retcode = data["local"]["retcode"]
remote_retcode = data["local"]["retcode"]
try:
retcode = int(remote_retcode)
except (TypeError, ValueError):
log.warning(
f"Host '{host}' reported an invalid retcode: '{remote_retcode}'"
)
retcode = max(retcode, 1)
except (KeyError, TypeError):
pass
else:
Expand Down Expand Up @@ -807,6 +814,9 @@ def run(self, jid=None):
final_exit = 0
for ret, retcode in self.handle_ssh():
host = next(iter(ret))
if not isinstance(retcode, int):
log.warning(f"Host '{host}' returned an invalid retcode: {retcode}")
retcode = 1
final_exit = max(final_exit, retcode)

self.cache_job(jid, host, ret[host], fun)
Expand Down
25 changes: 24 additions & 1 deletion tests/pytests/unit/client/ssh/test_ssh.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import salt.client.ssh.client
import salt.utils.msgpack
from salt.client import ssh
from tests.support.mock import MagicMock, patch
from tests.support.mock import MagicMock, Mock, patch

pytestmark = [
pytest.mark.skip_if_binaries_missing("ssh", "ssh-keygen", check_all=True),
Expand Down Expand Up @@ -339,3 +339,26 @@ def test_extra_filerefs(tmp_path, opts):
with patch("salt.roster.get_roster_file", MagicMock(return_value=roster)):
ssh_obj = client._prep_ssh(**ssh_opts)
assert ssh_obj.opts.get("extra_filerefs", None) == "salt://foobar"


def test_handle_routine_remote_invalid_retcode(opts, target, caplog):
"""
Ensure that if a remote returns an invalid retcode as part of the return dict,
the final exit code is still an integer and set to 1 at least.
"""
single_ret = ('{"local": {"retcode": null, "return": "foo"}}', "", 0)
opts["tgt"] = "localhost"
single = MagicMock(spec=ssh.Single)
single.id = "localhost"
single.run.return_value = single_ret
que = Mock()

with patch("salt.roster.get_roster_file", MagicMock(return_value="")), patch(
"salt.client.ssh.Single", autospec=True, return_value=single
):
client = ssh.SSH(opts)
client.handle_routine(que, opts, "localhost", target)
que.put.assert_called_once_with(
({"id": "localhost", "ret": {"retcode": None, "return": "foo"}}, 1)
)
assert "Host 'localhost' reported an invalid retcode: 'None'" in caplog.text

0 comments on commit 4ee3ef4

Please sign in to comment.