class Really::Drivers::SSH

Constants

KILOBYTE
SCP_CHUNK_SIZE

Public Class Methods

new(*args) click to toggle source
Calls superclass method Really::Drivers::DriverBase::new
# File lib/really/drivers/ssh.rb, line 13
def initialize(*args)
  @user = "root"
  @hosts = []
  @port = 22
  @keys = '~/.ssh/id_rsa'
  @connections = {}

  # Super does an instance_eval on the given block, so we need to call
  # super *after* setting up our internal state.
  super *args
end

Public Instance Methods

close() click to toggle source
# File lib/really/drivers/ssh.rb, line 68
def close
  @connections.values.each { |connection| connection.close }
  @connection_gateway.shutdown! if @connection_gateway
end
gateway(gateway) click to toggle source
# File lib/really/drivers/ssh.rb, line 48
def gateway(gateway)
  @gateway = gateway
end
host(*hosts)
Alias for: hosts
hosts(*hosts) click to toggle source
# File lib/really/drivers/ssh.rb, line 31
def hosts(*hosts)
  @hosts += hosts
end
Also aliased as: host
keys(keys) click to toggle source
# File lib/really/drivers/ssh.rb, line 44
def keys(keys)
  @keys = File.expand_path keys
end
open() click to toggle source
# File lib/really/drivers/ssh.rb, line 52
def open
  @hosts.each do |host|
    key = connection_key @user, host, @port
    options = { password: @password, keys: @keys, port: @port }
    options.delete :password unless @password
    options.delete :keys unless @keys

    if @gateway
      @connection_gateway ||= Net::SSH::Gateway.new @gateway, @user
      @connections[key] ||= gateway.ssh host, @user, options
    else
      @connections[key] ||= Net::SSH.start host, @user, options
    end
  end
end
password(password) click to toggle source
# File lib/really/drivers/ssh.rb, line 40
def password(password)
  @password = password
end
port(port) click to toggle source
# File lib/really/drivers/ssh.rb, line 36
def port(port)
  @port = port.to_i
end
user(user) click to toggle source

Public API for use in ‘driver` blocks in really.rb scripts

# File lib/really/drivers/ssh.rb, line 27
def user(user)
  @user = user
end

Protected Instance Methods

execute_command(command, options = {}) click to toggle source

Required overrides from Really::Drivers::DriverBase

# File lib/really/drivers/ssh.rb, line 77
def execute_command(command, options = {})
  @connections.values.each do |connection|
    stdout_data = StringScanner.new ""
    stderr_data = StringScanner.new ""

    connection.open_channel do |channel|
      channel.on_data do |channel, data|
        stdout_data << data
        while (line = stdout_data.scan_until(/\n/))
          if options[:log_output]
            logger.status line, header: :arrow, header_color: :yellow
          else
            logger.debug line, header: :arrow, header_color: :yellow
          end
        end
      end

      channel.on_extended_data do |channel, type, data|
        stderr_data << data
        while (line = stderr_data.scan_until(/\n/))
          if options[:log_output]
            logger.status line, header: :arrow, header_color: :red
          else
            logger.debug line, header: :arrow, header_color: :red
          end
        end
      end

      channel.on_request("exit-status") do |channel, data|
        command_exited_with_exit_code data.read_long, options
      end

      channel.on_request("exit-signal") do |channel, data|
        logger.error "Command #{command} signaled: #{data.read_long}"
      end

      channel.exec(command.command) do |channel, success|
        raise "Unable to execute remote command #{command}." unless success
      end
    end

    logger.status stdout_data.rest, header: :arrow unless stdout_data.eos?
    logger.error stderr_data.rest, header: :arrow unless stderr_data.eos?

    connection.loop
  end
end
transfer_file(source_path, destination_path, options = {}) click to toggle source
# File lib/really/drivers/ssh.rb, line 125
def transfer_file(source_path, destination_path, options = {})
  @connections.values.each do |connection|
    begin
      logger.debug "Transferring '#{source_path}' to '#{destination_path}' via SCP..."
      scp = Net::SCP.new connection
      scp.upload! source_path, destination_path, recursive: options[:recursive], chunk_size: SCP_CHUNK_SIZE
    rescue RuntimeError => error
      raise "File transfer failed: '#{source_path}' => '#{destination_path}' (permission denied)." if error.message =~ /Permission denied/
      raise error
    end
  end
end

Private Instance Methods

connection_key(user, host, port) click to toggle source
# File lib/really/drivers/ssh.rb, line 140
def connection_key(user, host, port)
  "#{user}@#{host}:#{port}"
end