def test_rcd(): "`operations.rcd` causes the command to be executed to happen in a different directory" with patch("threadbare.operations._execute") as mockobj: with operations.rcd("/tmp"): mockobj.return_value = { "return_code": lambda: 0, "stdout": [], "stderr": [], } operations.remote("pwd", host_string=HOST, port=PORT, user=USER, key_filename=PEM) expected_kwargs = { "host_string": HOST, "port": PORT, "user": USER, "key_filename": PEM, "use_pty": True, "timeout": None, "command": '/bin/bash -l -c "cd \\"/tmp\\" && pwd"', } mockobj.assert_called_with(**expected_kwargs)
def test_run_a_remote_command_non_zero_custom_exit(): """`remote` commands, like `local` commands, may raise a custom exception if the command they execute fails. the results of the command are still available via the `result` attribute on the exception object""" with test_settings(): with pytest.raises(ValueError) as err: remote("exit 123", abort_exception=ValueError) exc = err.value assert exc.result["return_code"] == 123 assert exc.result["failed"] assert not exc.result["succeeded"]
def test_run_a_remote_command_with_shell_interpolation(): "run a simple `remote` command including shell variables" with test_settings(quiet=True): result = remote('foo=baz; echo "bar? $foo!"') assert result["succeeded"] assert ["bar? baz!"] == result["stdout"] result2 = remote('foo=baz; echo "bar? $foo!"', use_shell=False) assert result2["succeeded"] assert ["bar? baz!"] == result["stdout"]
def test_remote_command_exception(): kwargs = { "host_string": HOST, "port": PORT, "user": USER, "key_filename": PEM, "command": "echo hello", } m = mock.MagicMock() m.run_command = mock.Mock(side_effect=ValueError("earthshatteringkaboom")) with patch("threadbare.operations.SSHClient", return_value=m): with pytest.raises(operations.NetworkError): operations.remote(**kwargs)
def test_run_script(): "a simple shell script can be uploaded and executed and the results accessible" with empty_local_fixture() as local_env: with empty_remote_fixture() as remote_env: with test_settings(): local_script = join(local_env["temp-dir"], "script.sh") open(local_script, "w").write(r"""#!/bin/bash echo "hello, world" """) remote_script = join(remote_env["temp-dir"], "script.sh") upload(local_script, remote_script) remote("chmod +x %s" % remote_script) with rcd(os.path.dirname(remote_script)): result = remote("./script.sh") assert ["hello, world"] == result["stdout"]
def test_download_to_extant_local_file(): "the default policy is to overwrite files that exist." with local_fixture() as local_env: with empty_remote_fixture() as remote_env: with test_settings(): local_file = local_env["temp-files"]["small-file"] assert os.path.exists(local_file) payload = "foo" remote_file = join(remote_env["temp-dir"], "foo.file") remote('printf %s > "%s"' % (payload, remote_file)) assert remote_file_exists(remote_file) download(remote_file, local_file) result = open(local_file, "r").read() assert payload == result
def workerfn(): iterations = 2 cmd = 'for run in {1..%s}; do echo "I am %s, iteration $run"; done' % ( iterations, state.ENV["worker_num"], ) return remote(cmd)
def test_remote_command_timeout_exception(): kwargs = { "host_string": HOST, "port": PORT, "user": USER, "key_filename": PEM, "command": "echo hello", } m = mock.MagicMock() m.run_command = mock.Mock(side_effect=pssh_exceptions.Timeout("foobar")) with patch("threadbare.operations.SSHClient", return_value=m): with pytest.raises(operations.NetworkError) as err: operations.remote(**kwargs) err = err.value assert type(err.wrapped) == pssh_exceptions.Timeout assert str(err) == "Timed out trying to connect. foobar"
def test_run_a_remote_command_non_zero_return_code_swallow_error(): "`remote` commands, like `local` commands, can return the results of failed executions when `warn_only` is `True`" with test_settings(warn_only=True): result = remote("exit 123") assert result["return_code"] == 123 assert result["failed"] assert not result["succeeded"]
def test_run_a_remote_command_but_hide_output(): "run a simple `remote` command but don't print anything" with test_settings(): with hide(): result = remote("echo hi!") # (nothing should have been emitted) assert result["succeeded"] assert result["stdout"] == ["hi!"]
def test_run_a_remote_command_in_a_different_dir(): "run a simple `remote` command in a different remote directory" with remote_fixture() as remote_env: with test_settings(): remote_dir = remote_env["temp-dir"] with rcd(remote_dir): result = remote("pwd") assert result["succeeded"] assert [remote_dir] == result["stdout"]
def test_run_a_remote_command_with_separate_streams(): "run a simple `remote` command and capture stdout and stderr separately" with test_settings(): result = remote( 'echo "printed to standard out"; >&2 echo "printed to standard error"', combine_stderr=False, ) assert result["succeeded"] assert ["printed to standard out"] == result["stdout"] assert ["printed to standard error"] == result["stderr"]
def test_run_many_remote_commands_singly(): "multiple commands can be concatenated into a single command" command_list = [ "echo all", "echo these commands", "echo are executed", "echo together", ] with test_settings(): result = remote(single_command(command_list)) assert result["succeeded"]
def test_run_many_remote_commands(): "running many `remote` commands re-uses the established ssh session" command_list = [ "echo all", "echo these commands", "echo share the same", "echo ssh session", ] with test_settings(): for command in command_list: result = remote(command) assert result["succeeded"]
def test_upload_to_extant_remote_file(): "the default policy is to overwrite files that exist." with empty_local_fixture(): with remote_fixture() as remote_env: with test_settings(): payload = b"foo" remote_file = remote_env["temp-files"]["small-file"] # just to illustrate an overwrite *is* happening assert remote_file_exists(remote_file) upload(BytesIO(payload), remote_file) result = remote('cat "%s"' % (remote_file, )) assert [payload.decode("utf-8")] == result["stdout"]
def _test_upload_and_download_a_file(transfer_protocol): """write a local file, upload it to the remote server, modify it remotely, download it, modify it locally, assert it's contents are as expected""" with empty_local_fixture() as local_env: with empty_remote_fixture() as remote_env: with test_settings(transfer_protocol=transfer_protocol): LOG.debug("modifying local file ...") local_file_name = join(local_env["temp-dir"], "foo") local('printf "foo" > %s' % local_file_name) LOG.debug("uploading file ...") remote_file_name = join(remote_env["temp-dir"], "foobar") upload(local_file_name, remote_file_name) # verify contents assert remote_file_exists(remote_file_name) assert remote("cat %s" % remote_file_name)["stdout"] == ["foo"] LOG.debug("modifying remote file ...") remote('printf "bar" >> %s' % remote_file_name) # verify contents assert remote("cat %s" % remote_file_name)["stdout"] == ["foobar"] LOG.debug("downloading file ...") new_local_file_name = join(local_env["temp-dir"], "foobarbaz") download(remote_file_name, new_local_file_name) # verify contents assert open(new_local_file_name, "r").read() == "foobar" LOG.debug("modifying local file (again) ...") local('printf "baz" >> %s' % new_local_file_name) LOG.debug("testing local file ...") data = open(new_local_file_name, "r").read() assert "foobarbaz" == data
def test_remote_args_to_execute(): "`operations.remote` calls `operations._execute` with the correct arguments" with patch("threadbare.operations._execute") as mockobj: mockobj.return_value = { "return_code": lambda: 0, "stdout": [], "stderr": [] } operations.remote("echo hello", host_string=HOST, port=PORT, user=USER, key_filename=PEM) expected_kwargs = { "host_string": HOST, "port": PORT, "user": USER, "key_filename": PEM, "use_pty": True, "timeout": None, "command": '/bin/bash -l -c "echo hello"', } mockobj.assert_called_with(**expected_kwargs)
def test_upload_and_download_a_file_using_byte_buffers(): """contents of a BytesIO buffer can be uploaded to a remote file, and the contents of a remote file can be downloaded to a BytesIO buffer""" with empty_remote_fixture() as remote_env: with test_settings(quiet=True): payload = b"foo-bar-baz" uploadable_unicode_buffer = BytesIO(payload) remote_file_name = join(remote_env["temp-dir"], "bytes-test") upload(uploadable_unicode_buffer, remote_file_name) assert remote_file_exists(remote_file_name) result = remote('cat "%s"' % remote_file_name) assert result["succeeded"] assert result["stdout"] == [payload.decode()] download_unicode_buffer = BytesIO() download(remote_file_name, download_unicode_buffer) assert download_unicode_buffer.getvalue() == payload
def workerfn(): upload(local_script, remote_script) remote("chmod +x %s" % remote_script) with rcd(os.path.dirname(remote_script)): return remote("./script.sh")
def test_run_a_remote_command(): "run a simple `remote` command" with test_settings(quiet=True): result = remote(r'echo -e "\e[31mRed Text!!\e[0m"') assert result["succeeded"]
def test_remote_non_default_args(): "`operations.remote` calls `operations._execute` with the correct arguments" base = { "host_string": HOST, "port": PORT, "user": USER, "key_filename": PEM, "command": "echo hello", "timeout": None, } # given args, expected args cases = [ # non-shell regular command [{ "use_shell": False }, { "use_pty": True, "command": "echo hello" }], # non-shell, non-tty command [ { "use_shell": False, "combine_stderr": False }, { "use_pty": False, "command": "echo hello" }, ], # non-shell sudo command [ { "use_shell": False, "use_sudo": True }, { "use_pty": True, "command": "sudo --non-interactive echo hello" }, ], # shell, regular command [ { "use_shell": True }, { "use_pty": True, "command": '/bin/bash -l -c "echo hello"' }, ], # shell, sudo command [ { "use_shell": True, "use_sudo": True }, { "use_pty": True, "command": 'sudo --non-interactive /bin/bash -l -c "echo hello"', }, ], # shell escaped operations [ { "command": 'foo=bar; echo "bar? $foo!"' }, { "use_pty": True, "command": '/bin/bash -l -c "foo=bar; echo \\"bar? \\$foo!\\""', }, ], # shell escaped operations, non-shell [ { "command": 'foo=bar; echo "bar? $foo!"', "use_shell": False }, { "use_pty": True, "command": 'foo=bar; echo "bar? $foo!"' }, ], # specific directory [ { "remote_working_dir": "/tmp", "command": "pwd", "use_shell": False }, { "use_pty": True, "command": 'cd "/tmp" && pwd' }, ], [ { "remote_working_dir": "/tmp", "command": "pwd", "use_shell": True }, { "use_pty": True, "command": '/bin/bash -l -c "cd \\"/tmp\\" && pwd"' }, ], # timeout [ { "command": "sleep 5", "timeout": 1 }, { "use_pty": True, "command": '/bin/bash -l -c "sleep 5"', "timeout": 1 }, ], # edge cases # shell, non-tty command # in order to combine output streams, `pty` must be off [ { "use_pty": False }, { "use_pty": True, "command": '/bin/bash -l -c "echo hello"' }, # !! ], # if you really need a `pty`, you'll have to handle separate stdout/stderr streams [ { "use_pty": False, "combine_stderr": False }, { "use_pty": False, "command": '/bin/bash -l -c "echo hello"' }, ], ] for given_kwargs, expected_kwargs in cases: with patch("threadbare.operations._execute") as mockobj: mockobj.return_value = { "return_code": lambda: 0, "stdout": [], "stderr": [], } operations.remote(**merge(base, given_kwargs)) mockobj.assert_called_with(**merge(base, expected_kwargs))
def myfn(): return remote(state.ENV["cmd"], capture=True)
def workerfn(): with state.settings(): return remote("exit 1")