class TrickBag::Networking::SshOutputReader

Runs an ssh command, collecting the stdout and stderr produced by it. Optionally a predicate can be specified that, when called with the instance of this class, returns true to close the channel or false to continue.

There are instance methods to return the most recent and all accumulated text for both stdout and stderr, so the predicate can easily inspect the text.

While the predicate will most likely be a lambda, it can be any object that responds to call().

Attributes

all_stderr[RW]
all_stdout[RW]
channel[RW]
command[R]
exit_predicate[R]
host[R]
latest_output_type[RW]
latest_stderr[RW]
latest_stdout[RW]
password[R]
user[R]

Public Class Methods

new(host, command, user = ENV['USER'], password = nil) click to toggle source
# File lib/trick_bag/networking/ssh_output_reader.rb, line 22
def initialize(host, command, user = ENV['USER'], password = nil)
  @host = host
  @user = user
  @password = password
  @command = command
  @exit_predicate = ->(instance_of_this_class) { false }
  clear_buffers
end

Public Instance Methods

clear_buffers() click to toggle source
# File lib/trick_bag/networking/ssh_output_reader.rb, line 32
def clear_buffers
  @latest_stdout = ''
  @all_stdout = ''
  @latest_stderr = ''
  @all_stderr = ''
end
close() click to toggle source

The channel will automatically be closed if the predicate returns false or if the command completes. This method is provided for cases where the predicate wants to raise an error; close should be called before raising the error.

# File lib/trick_bag/networking/ssh_output_reader.rb, line 108
def close
  channel.close if channel
end
run() click to toggle source

Runs the specified command. If an exit predicate has been specified via set_exit_predicate, then it will be called whenever anything is output to stdout or stderr, and the channel will be closed if the predicate returns true.

@return self, to support chaining

# File lib/trick_bag/networking/ssh_output_reader.rb, line 66
def run
  clear_buffers
  Net::SSH.start(host, user, password: password) do |ssh|

    # Open a new channel and configure a minimal set of callbacks, then run
    # the event loop until the channel finishes (closes).
    self.channel = ssh.open_channel do |ch|
      ch.exec(command) do |ch, success|
        raise "could not execute command" unless success

        # "on_data" is called when the process writes something to stdout
        ch.on_data do |c, data|
          self.latest_output_type = :stdout
          self.latest_stdout = data
          self.all_stdout << data
          $stdout.print data
          ch.close if exit_predicate.(self)
        end

        # "on_extended_data" is called when the process writes something to stderr
        ch.on_extended_data do |c, type, data|
          self.latest_output_type = :stderr
          self.latest_stderr = data
          self.all_stderr << data
          $stderr.print data
          ch.close if exit_predicate.(self)
        end

        ch.on_close { self.channel = nil; return self }
      end
    end

    channel.wait
    self.channel = nil
    self
  end
end
set_exit_predicate(exit_predicate) click to toggle source

Sets a predicate that, when called with this reader instance, returns true to close the channel and return, or false to permit the channel to continue operating.

Using this self value, any instance methods can be called; the most useful will probably be latest_stdout, all_stdout, latest_stderr, and all_stderr. There is also a latest_output_type that returns :stdout or :stderr to indicate which type of output just occurred.

For example, to exit when the string “exit” is sent to stdout: reader.set_exit_predicate(->(reader) { /exit/ === reader.latest_stdout })

Returns self to facilitate chaining, e.g.: stdout, stderr = SshOutputReader.new(...).set_exit_predicate(…).run

# File lib/trick_bag/networking/ssh_output_reader.rb, line 54
def set_exit_predicate(exit_predicate)
  @exit_predicate = exit_predicate
  self
end
to_s() click to toggle source
# File lib/trick_bag/networking/ssh_output_reader.rb, line 113
def to_s
  "host = #{host}, command = #{command}, user = #{user}" # password intentionally omitted
end