Module: SshExec

Defined in:
lib/ssh-exec.rb,
lib/ssh-exec/version.rb

Defined Under Namespace

Classes: ExecutionError

Constant Summary collapse

VERSION =
IO.read(File.expand_path("../../../VERSION", __FILE__)).strip
@@log =
Logger.new(STDOUT)

Class Method Summary collapse

Class Method Details

.ensure_exec(ssh, command, options = {}) ⇒ OpenStruct

Runs the given command in the given SSH shell, and raises ExecutionError in case of failure (nonzero exit status). Logs the command in all cases. Logs the command, exit status, standard output and standard error in case of failure.

Parameters:

  • ssh

    the Net::SSH shell to run the command in

  • options (Hash) (defaults to: {})

    optional settings: echo_stdout - whether to echo stdout from the subcommand, echo_stderr - whether to echo stderr from the subcommand, quiet - to suppress logging the command and the error in case of non-zero exit status

Returns:

  • (OpenStruct)

    a struct containing stdout, stderr, exit_status, and exit_signal



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/ssh-exec.rb', line 82

def self.ensure_exec(ssh, command, options = {})
  result = ssh_exec!(ssh, command, options)

  options = options.clone
  quiet = options.delete(:quiet)

  @@log.info("Running on #{ssh.host}: #{command}") unless quiet

  if result.exit_status  != 0
    @@log.error(
      ("Failed running command #{command}: exit status #{result.exit_status}. " +
      (result.stdout.empty? ? "" : "Standard output:\n#{result.stdout}\n") +
      (result.stderr.empty? ? "" : "Standard error:\n#{result.stderr}")).strip
    ) unless quiet
    raise ExecutionError.new(
      "Failed running command #{command}: exit status #{result.exit_status}"
    )
  end
  result
end

.ssh_exec!(ssh, command, options = {}) ⇒ OpenStruct

Execute the given command using the given ssh object and capture its standard output, standard error, and exit status. The implementation is based on this StackOveflow answer.

Parameters:

  • ssh

    the Net::SSH shell to run the command in

  • options (Hash) (defaults to: {})

    optional settings: echo_stdout - whether to echo standard output from the subcommand, echo_stderr - whether to echo standard error from the subcommand

Returns:

  • (OpenStruct)

    a struct containing stdout, stderr, exit_status, and exit_signal



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/ssh-exec.rb', line 30

def self.ssh_exec!(ssh, command, options = {})
  options = options.clone
  echo_stdout = options[:echo_stdout]
  echo_stderr = options[:echo_stderr]
  raise "Invalid options: #{options}" unless options.empty?

  stdout_data = ""
  stderr_data = ""
  exit_code = nil
  exit_signal = nil
  ssh.open_channel do |channel|
    channel.exec(command) do |ch, success|
      unless success
        raise "FAILED: couldn't execute command #{command}"
      end
      channel.on_data do |ch, data|
        stdout_data += data
        $stdout.write(data) if echo_stdout
      end

      channel.on_extended_data do |ch, type, data|
        stderr_data += data
        $stderr.write(data) if echo_stderr
      end

      channel.on_request("exit-status") do |ch, data|
        exit_code = data.read_long
      end

      channel.on_request("exit-signal") do |ch, data|
        exit_signal = data.read_long
      end
    end
  end
  ssh.loop
  OpenStruct.new(
    :stdout => stdout_data,
    :stderr => stderr_data,
    :exit_status => exit_code,
    :exit_signal => exit_signal
  )
end