From 7ebad663481cafceaefe405578fac4816aef54b5 Mon Sep 17 00:00:00 2001 From: Aleksei Slaikovskii Date: Dec 15 2017 10:26:17 +0000 Subject: Complete fix for OpenSSH transport and freeze fix for Paramiko This patch fixes OpenSSH transport. Also, it happened it should fix tests execution freezes when stdin doesn't get closed while using Paramiko transport. --- diff --git a/Makefile b/Makefile index 3735918..0405338 100644 --- a/Makefile +++ b/Makefile @@ -49,6 +49,13 @@ srpm: tarball cp ${TARBALLNAME} rpmbuild/SOURCES/ rpmbuild --define "_topdir ${PWD}/rpmbuild" -bs ${FEDORA_PROJECT}.spec +rpm: tarball + rm -rvf rpmbuild + mkdir -p rpmbuild/SOURCES + mkdir -p rpmbuild/SRPMS + cp ${TARBALLNAME} rpmbuild/SOURCES/ + rpmbuild --define "_topdir ${PWD}/rpmbuild" -bb ${FEDORA_PROJECT}.spec + mock: srpm cp $(TARBALLNAME) $$(rpm -E '%{_topdir}')/SOURCES mock rebuild rpmbuild/SRPMS/*.src.rpm diff --git a/pytest_multihost/host.py b/pytest_multihost/host.py index a095a43..9d035c5 100644 --- a/pytest_multihost/host.py +++ b/pytest_multihost/host.py @@ -233,6 +233,11 @@ class BaseHost(object): if self.command_prelude: command.stdin.write(self.command_prelude) + if stdin_text: + command.stdin.write('echo -e "') + command.stdin.write(stdin_text.replace('"', '\\\"')) + command.stdin.write('" | ') + if isinstance(argv, basestring): # Run a shell command given as a string command.stdin.write('(') @@ -244,16 +249,13 @@ class BaseHost(object): command.stdin.write(shell_quote(arg)) command.stdin.write(' ') - command.stdin.write(';exit\n') - if stdin_text: - command.stdin.write(stdin_text) + command.stdin.write('\nexit\n') command.stdin.flush() if not bg: command.wait(raiseonerr=raiseonerr) return command - class Host(BaseHost): """A Unix host""" command_prelude = 'set -e\n' diff --git a/pytest_multihost/transport.py b/pytest_multihost/transport.py index b8984e3..d5ba7ac 100644 --- a/pytest_multihost/transport.py +++ b/pytest_multihost/transport.py @@ -11,6 +11,7 @@ OpenSSHTransport (if Paramiko is not importable, or the PYTESTMULTIHOST_SSH_TRANSPORT environment variable is set to "openssh"). """ +import base64 import os import socket import threading @@ -364,10 +365,10 @@ class OpenSSHTransport(Transport): def get_file_contents(self, filename, encoding=None): self.log.info('GET %s', filename) - cmd = self._run(['cat', filename], log_stdout=False) + cmd = self._run(['base64', filename], log_stdout=False) cmd.wait(raiseonerr=False) if cmd.returncode == 0: - result = cmd.stdout_text + result = base64.b64decode(cmd.stdout_text) if encoding: result = result.decode(encoding) return result diff --git a/test_pytestmultihost/test_localhost.py b/test_pytestmultihost/test_localhost.py index 180aa73..5edfeff 100644 --- a/test_pytestmultihost/test_localhost.py +++ b/test_pytestmultihost/test_localhost.py @@ -18,6 +18,7 @@ except ImportError: class AuthenticationException(Exception): """Never raised""" + def get_conf_dict(): return { 'ssh_username': getpass.getuser(), @@ -51,6 +52,7 @@ def get_conf_dict(): ], } + @pytest.fixture(scope='class', params=['paramiko', 'openssh']) def transport_class(request): if request.param == 'paramiko': @@ -60,6 +62,7 @@ def transport_class(request): else: raise ValueError('bad transport_class') + @pytest.fixture(scope='class') def multihost(request, transport_class): conf = get_conf_dict() @@ -80,6 +83,7 @@ def multihost(request, transport_class): assert isinstance(mh.host.transport, transport_class) return mh.install() + @pytest.fixture(scope='class') def multihost_baduser(request, transport_class): conf = get_conf_dict() @@ -98,6 +102,7 @@ def multihost_baduser(request, transport_class): mh.host.transport_class = transport_class return mh.install() + @pytest.fixture(scope='class') def multihost_badpassword(request, transport_class): conf = get_conf_dict() @@ -123,7 +128,7 @@ def _first_command(host): try: yield except (AuthenticationException, CalledProcessError): - print ( + print( 'Cannot login to %s using default SSH key (%s), user %s. ' 'You might want to add your own key ' 'to ~/.ssh/authorized_keys.' @@ -140,7 +145,7 @@ class TestLocalhost(object): host = multihost.host with _first_command(host): echo = host.run_command(['echo', 'hello', 'world']) - assert echo.stdout_text == 'hello world\n' + assert 'hello world\n' in echo.stdout_text def test_put_get_file_contents(self, multihost, tmpdir): host = multihost.host @@ -196,39 +201,61 @@ class TestLocalhost(object): host.transport.rmdir(filename) assert not os.path.exists(filename) + def test_background(self, multihost): + host = multihost.host + + host.run_command("echo bla > /tmp/testfile.in") + + run_nc = 'nc -k -l 12080 > /tmp/filename.out' + host.run_command(run_nc, bg=True, raiseonerr=False) + + send_file = ( + 'if [[ $(ps aux | grep "nc -l 18999" | grep -v grep | ' + 'wc -l) -eq 1 ]]; then echo "bla" ; else sleep 2; fi ; ' + 'nc localhost 12080 < /tmp/testfile.in' + ) + client = host.run_command(send_file) + assert client.returncode == 0 + + def test_escaping(self, multihost): + host = multihost.host + test_file_path = "/tmp/filename.in" + + stdin_text = '"test", test, "test"' + tee = host.run_command( + ["tee", test_file_path], + stdin_text=stdin_text + ) + with open(test_file_path, "r") as f: + assert f.read() == tee.stdout_text + def test_reset(self, multihost): host = multihost.host with _first_command(host): echo = host.run_command(['echo', 'hello', 'world']) - assert echo.stdout_text == 'hello world\n' + assert 'hello world\n' in echo.stdout_text host.ssh_password = 'BAD PASSWORD' host.ssh_key_filename = None echo = host.run_command(['echo', 'hello', 'world']) - assert echo.stdout_text == 'hello world\n' + assert 'hello world\n' in echo.stdout_text host.reset_connection() + host.ssh_password = None with pytest.raises((AuthenticationException, RuntimeError)): echo = host.run_command(['echo', 'hello', 'world']) - def test_baduser(self, multihost_baduser, tmpdir): host = multihost_baduser.host if host.transport_class == pytest_multihost.transport.OpenSSHTransport: # Avoid the OpenSSH password prompt return with pytest.raises(AuthenticationException): - echo = host.run_command(['echo', 'hello', 'world']) + host.run_command(['echo', 'hello', 'world']) def test_badpassword(self, multihost_badpassword, tmpdir): host = multihost_badpassword.host with pytest.raises((AuthenticationException, RuntimeError)): - echo = host.run_command(['echo', 'hello', 'world']) + host.run_command(['echo', 'hello', 'world']) + - def test_background(self, multihost): - host = multihost.host - run_nc = 'nc -l 12080 > /tmp/filename.out' - cmd = host.run_command(run_nc, bg=True, raiseonerr=False) - send_file = 'nc localhost 12080 < /root/anaconda-ks.cfg' - cmd = host.run_command(send_file) - assert cmd.returncode == 0