From 13430261ff2adcc3d929862cdbbdfb14eb1ea8cd Mon Sep 17 00:00:00 2001 From: Isaac Betesh Date: Wed, 12 Aug 2015 10:08:01 -0400 Subject: [PATCH] introduced a test_and_capture method for backends --- CHANGELOG.md | 3 +++ README.md | 14 +++++++++----- lib/sshkit/backends/abstract.rb | 22 ++++++++++++++++++++++ test/unit/backends/test_abstract.rb | 19 +++++++++++++++++++ 4 files changed, 53 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 23d8fcb3..87d62982 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ appear at the top. * Add your entries below here, remember to credit yourself however you want to be credited! + * Introduced `test_and_capture` method on backends + [PR #274](https://github.com/capistrano/sshkit/pull/274) + @betesh * Export environment variables and execute command in a subshell. [PR #273](https://github.com/capistrano/sshkit/pull/273) @kuon diff --git a/README.md b/README.md index fb6e0558..f538198d 100644 --- a/README.md +++ b/README.md @@ -37,11 +37,11 @@ parallel. #### Running commands -All backends support the `execute(*args)`, `test(*args)` & `capture(*args)` methods +All backends support the `execute(*args)`, `test(*args)`, `capture(*args)` & `test_and_capture(*args)` methods for executing a command. You can call any of these methods in the context of an `on()` block. -**Note: In SSHKit, the first parameter of the `execute` / `test` / `capture` methods +**Note: In SSHKit, the first parameter of the `execute` / `test` / `capture` / `test_and_capture` methods has a special significance. If the first parameter isn't a Symbol, SSHKit assumes that you want to execute the raw command and the `as` / `within` / `with` methods, `SSHKit.config.umask` and [the comand map](#the-command-map) @@ -55,10 +55,14 @@ on '1.example.com' execute(:cp, 'somefile.txt', 'somewhere_else.txt') end ls_output = capture(:ls, '-l') + rm_output = test_and_capture(:rm, "/a/file") + unless rm_output.success? + raise StandardError, rm_output.stderr + rm_output.stdout + end end ``` -By default the `capture` methods strips whitespace. If you need to preserve whitespace +By default the `capture` & `test_and_capture` methods strip whitespace. If you need to preserve whitespace you can pass the `strip: false` option: `capture(:ls, '-l', strip: false)` #### Transferring files @@ -267,7 +271,7 @@ Wherever possible, you should call commands in a way that doesn't require intera (eg by specifying all options as command arguments). However in some cases, you may want to programmatically drive interaction with a command -and this can be achieved by specifying an `:interaction_handler` option when you `execute`, `capture` or `test` a command. +and this can be achieved by specifying an `:interaction_handler` option when you `execute`, `capture`, `test`, or `test_and_capture` a command. **It is not necessary, or desirable to enable `Netssh.config.pty` to use the `interaction_handler` option. Only enable `Netssh.config.pty` if the command you are calling won't work without a pty.** @@ -450,7 +454,7 @@ SSHKit.config.format = :foobar ## Output Verbosity -By default calls to `capture()` and `test()` are not logged, they are used +By default calls to `test()`, `capture()` and `test_and_capture()` are not logged, they are used *so* frequently by backend tasks to check environmental settings that it produces a large amount of noise. They are tagged with a verbosity option on the `Command` instances of `Logger::DEBUG`. The default configuration for diff --git a/lib/sshkit/backends/abstract.rb b/lib/sshkit/backends/abstract.rb index 727c3ca4..8a7b3c29 100644 --- a/lib/sshkit/backends/abstract.rb +++ b/lib/sshkit/backends/abstract.rb @@ -1,6 +1,17 @@ module SSHKit module Backend + class CapturedResult + attr_reader :stdout, :stderr + + def initialize(success, stdout, stderr) + @success, @stdout, @stderr = success, stdout, stderr + end + + def success? + @success + end + end MethodUnavailableError = Class.new(SSHKit::StandardError) @@ -40,6 +51,17 @@ def capture(*args) options[:strip] ? result.strip : result end + def test_and_capture(*args) + options = { verbosity: Logger::DEBUG, strip: true, raise_on_non_zero_exit: false }.merge(args.extract_options!) + cmd = create_command_and_execute(args, options) + stdout, stderr = cmd.full_stdout, cmd.full_stderr + if options[:strip] + stdout = stdout.strip + stderr = stderr.strip + end + CapturedResult.new(cmd.success?, stdout, stderr) + end + def background(*args) SSHKit.config.deprecation_logger.log( 'The background method is deprecated. Blame badly behaved pseudo-daemons!' diff --git a/test/unit/backends/test_abstract.rb b/test/unit/backends/test_abstract.rb index c53b6874..1519b6f9 100644 --- a/test/unit/backends/test_abstract.rb +++ b/test/unit/backends/test_abstract.rb @@ -64,6 +64,23 @@ def test_capture_creates_and_executes_command_and_returns_stripped_output assert_equal 'Some stdout', output end + def test_test_and_capture + output = nil + backend = ExampleBackend.new do + output = test_and_capture :cat, '/a/file' + end + backend.full_stdout = "Some stdout\n " + backend.full_stderr = "Some stderr\n " + + backend.run + + assert_equal '/usr/bin/env cat /a/file', backend.executed_command.to_command + assert_equal false, output.success? + assert_equal 'Some stdout', output.stdout + assert_equal 'Some stderr', output.stderr + assert_equal false, backend.executed_command.options[:raise_on_non_zero_exit], 'raise_on_non_zero_exit option' + end + def test_capture_supports_disabling_strip output = nil backend = ExampleBackend.new do @@ -121,6 +138,7 @@ def test_invoke_raises_no_method_error # Use a concrete ExampleBackend rather than a mock for improved assertion granularity class ExampleBackend < Abstract + attr_writer :full_stderr attr_writer :full_stdout attr_reader :executed_command @@ -132,6 +150,7 @@ def initialize(&block) def execute_command(command) @executed_command = command command.on_stdout(nil, @full_stdout) unless @full_stdout.nil? + command.on_stderr(nil, @full_stderr) unless @full_stderr.nil? end def ExampleBackend.example_host