Skip to content

Commit

Permalink
new safe_ssh backend to avoid bugs related to ssh and or ssh wrappers
Browse files Browse the repository at this point in the history
  • Loading branch information
philpep committed Mar 20, 2015
1 parent a63ccf6 commit d4e7349
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 2 deletions.
1 change: 1 addition & 0 deletions testinfra/backend/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def get_backend(backend_type, *args, **kwargs):
backend_class = {
"local": local.LocalBackend,
"ssh": ssh.SshBackend,
"safe_ssh": ssh.SafeSshBackend,
"paramiko": paramiko.ParamikoBakend,
}[backend_type]
except KeyError:
Expand Down
41 changes: 40 additions & 1 deletion testinfra/backend/ssh.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,14 @@

from __future__ import unicode_literals

import base64

from testinfra.backend import base
from testinfra.backend import local


class SshBackend(local.LocalBackend):
"""Run command through ssh command"""

def __init__(self, hostspec, *args, **kwargs):
self.host, self.user, self.port = self.parse_hostspec(hostspec)
Expand All @@ -36,7 +40,42 @@ def run(self, command, *args, **kwargs):
cmd.append("%s %s")
cmd_args.extend([
self.host,
self.quote(command, *args)
self.quote(command, *args),
])
return super(SshBackend, self).run(
" ".join(cmd), *cmd_args, **kwargs)


class SafeSshBackend(SshBackend):
"""Run command using ssh command but try to get a more sane output
When using ssh (or a potentially bugged wrapper) additional output can be
added in stdout/stderr and exit status may not be propagate correctly
To avoid that kind of bugs, we wrap the command to have an output like
this:
TESTINFRA_START;EXIT_STATUS;STDOUT;STDERR;TESTINFRA_END
where STDOUT/STDERR are base64 encoded, then we parse that magic string to
get sanes variables
"""

def run(self, command, *args, **kwargs):
orig_command = self.quote(command, *args)

out = super(SafeSshBackend, self).run((
'''of=$(mktemp)&&ef=$(mktemp)&&%s >$of 2>$ef; r=$?;'''
'''echo "TESTINFRA_START;$r;$(base64 < $of);$(base64 < $ef);'''
'''TESTINFRA_END";rm -f $of $ef''') % (orig_command,))

start = out.stdout.find("TESTINFRA_START;") + len("TESTINFRA_START;")
end = out.stdout.find("TESTINFRA_END") - 1
rc, stdout, stderr = out.stdout[start:end].split(";")

return base.CommandResult(
rc=int(rc),
stdout=base64.b64decode(stdout),
stderr=base64.b64decode(stderr),
command=orig_command,
)
2 changes: 1 addition & 1 deletion testinfra/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def pytest_addoption(parser):
"--connection",
action="store",
dest="connection",
help="Remote connection backend ssh|paramiko",
help="Remote connection backend ssh|paramiko|safe_ssh",
)
group._addoption(
"--hosts",
Expand Down

0 comments on commit d4e7349

Please sign in to comment.