module RHC::SSHHelpers

Public Instance Methods

discover_ssh_executable() click to toggle source

try my best to discover a ssh executable

# File lib/rhc/ssh_helpers.rb, line 432
def discover_ssh_executable
  @ssh_executable ||= begin
    guessing_locations = ['ssh']

    #:nocov:
    if RHC::Helpers.windows?
      # looks for ssh.exe from msysgit or plink.exe from PuTTY, either on path or specific locations
      guessing_locations << 
        discover_windows_executables do |base|
          [ 
            'ssh.exe', 
            "#{base}\\Git\\bin\\ssh.exe", 
            "#{base}\\ssh.exe", 
            'plink.exe',
            "#{base}\\PuTTY\\plink.exe",
            "#{base}\\plink.exe",
            'putty.exe',
            "#{base}\\PuTTY\\putty.exe",
            "#{base}\\putty.exe" 
          ]
        end
    end
    #:nocov:

    # make sure commands can be executed and finally pick the first one
    guessing_locations.flatten.uniq.select do |cmd| 
      (check_ssh_executable!(cmd).present? rescue false) && 
      (begin
        # putty -V exit as 1
        cmd =~ /plink\.exe/i || cmd =~ /putty\.exe/i || (begin 
          ssh_version(cmd)
          $?.success?
        end)
      rescue ; false ; end)
    end.collect{|cmd| cmd =~ / / ? '"' + cmd + '"' : cmd}.first
  end
fingerprint_for_default_key() click to toggle source
# File lib/rhc/ssh_helpers.rb, line 400
def fingerprint_for_default_key
  fingerprint_for_local_key(RHC::Config.ssh_pub_key_file_path)
end
fingerprint_for_local_key(key) click to toggle source
# File lib/rhc/ssh_helpers.rb, line 387
def fingerprint_for_local_key(key)
  Net::SSH::KeyFactory.load_public_key(key).fingerprint
rescue NoMethodError, NotImplementedError => e
  ssh_keygen_fallback key
  nil
rescue OpenSSL::PKey::PKeyError, Net::SSH::Exception => e
  error e.message
  nil
rescue => e
  debug e.message
  nil
end
generate_ssh_key_ruby(type="RSA", bits = 2048, comment = "OpenShift-Key") click to toggle source

Public: Generate an SSH key and store it in ~/.ssh/id_rsa

type - The String type RSA or DSS. bits - The Integer value for number of bits. comment - The String comment for the key

Examples

generate_ssh_key_ruby
# => /home/user/.ssh/id_rsa.pub

Returns nil on failure or public key location as a String on success

# File lib/rhc/ssh_helpers.rb, line 350
def generate_ssh_key_ruby(type="RSA", bits = 2048, comment = "OpenShift-Key")
  key = RHC::Vendor::SSHKey.generate(:type => type,
                                     :bits => bits,
                                     :comment => comment)
  ssh_dir = RHC::Config.ssh_dir
  priv_key = RHC::Config.ssh_priv_key_file_path
  pub_key = RHC::Config.ssh_pub_key_file_path

  if File.exists?(priv_key)
    say "SSH key already exists: #{priv_key}.  Reusing..."
    return nil
  else
    unless File.exists?(ssh_dir)
      FileUtils.mkdir_p(ssh_dir)
      File.chmod(0700, ssh_dir)
    end
    File.open(priv_key, 'w') {|f| f.write(key.private_key)}
    File.chmod(0600, priv_key)
    File.open(pub_key, 'w') {|f| f.write(key.ssh_public_key)}

    ssh_add
  end
  pub_key
end
has_ssh?() click to toggle source

return whether or not SSH is installed

# File lib/rhc/ssh_helpers.rb, line 427
def has_ssh?
  discover_ssh_executable.present?
end
restore_snapshot(app, filename, ssh_executable=nil) click to toggle source
# File lib/rhc/ssh_helpers.rb, line 289
def restore_snapshot(app, filename, ssh_executable=nil)
  include_git = RHC::Helpers.windows? ? true : RHC::TarGz.contains(filename, './*/git')
  ssh_uri = URI.parse(app.ssh_url)
  ssh_executable = check_ssh_executable! options.ssh

  ssh_cmd = "cat '#{filename}' | #{ssh_executable} #{ssh_uri.user}@#{ssh_uri.host} 'restore#{include_git ? ' INCLUDE_GIT' : ''}'"
  ssh_stderr = " 2>/dev/null"
  debug ssh_cmd

  say "Restoring from snapshot #{filename} to application '#{app.name}' ... "

  begin
    if !RHC::Helpers.windows?
      status, output = exec(ssh_cmd + (debug? ? '' : ssh_stderr))
      if status != 0
        debug output
        raise RHC::SnapshotRestoreException.new "Error in trying to restore snapshot. You can try to restore manually by running:\n#{ssh_cmd}"
      end
    else
      ssh = Net::SSH.start(ssh_uri.host, ssh_uri.user)
      ssh.open_channel do |channel|
        channel.exec("restore#{include_git ? ' INCLUDE_GIT' : ''}") do |ch, success|
          channel.on_data do |ch, data|
            debug data
          end
          channel.on_extended_data do |ch, type, data|
            debug data
          end
          channel.on_close do |ch|
            debug "Terminating..."
          end
          File.open(filename, 'rb') do |file|
            file.chunk(4096) do |chunk|
              channel.send_data chunk
            end
          end
          channel.eof!
        end
      end
      ssh.loop
    end
  rescue Timeout::Error, Errno::EADDRNOTAVAIL, Errno::EADDRINUSE, Errno::EHOSTUNREACH, Errno::ECONNREFUSED, Net::SSH::AuthenticationFailed => e
    debug e.backtrace
    raise RHC::SnapshotRestoreException.new "Error in trying to restore snapshot. You can try to restore manually by running:\n#{ssh_cmd}"
  end

  success 'done'
end
run_on_gears(command, gears, opts={}, &block) click to toggle source
# File lib/rhc/ssh_helpers.rb, line 134
def run_on_gears(command, gears, opts={}, &block)
  debug "Executing #{command} on each of #{gears.inspect}"
  MultipleGearTask.new(command, gears, {:limit => options.limit, :always_prefix => options.always_prefix, :raw => options.raw}.merge(opts)).run(&block)
end
save_snapshot(app, filename, for_deployment=false, ssh_executable=nil) click to toggle source
# File lib/rhc/ssh_helpers.rb, line 250
def save_snapshot(app, filename, for_deployment=false, ssh_executable=nil)
  ssh_uri = URI.parse(app.ssh_url)
  ssh_executable = check_ssh_executable! ssh_executable

  snapshot_cmd = for_deployment ? 'gear archive-deployment' : 'snapshot'
  ssh_cmd = "#{ssh_executable} #{ssh_uri.user}@#{ssh_uri.host} '#{snapshot_cmd}' > #{filename}"
  ssh_stderr = " 2>/dev/null"
  debug ssh_cmd

  say "Pulling down a snapshot of application '#{app.name}' to #{filename} ... "

  begin
    if !RHC::Helpers.windows?
        status, output = exec(ssh_cmd + (debug? ? '' : ssh_stderr))
        if status != 0
          debug output
          raise RHC::SnapshotSaveException.new "Error in trying to save snapshot. You can try to save manually by running:\n#{ssh_cmd}"
        end
    else
      Net::SSH.start(ssh_uri.host, ssh_uri.user) do |ssh|
        File.open(filename, 'wb') do |file|
          ssh.exec! snapshot_cmd do |channel, stream, data|
            if stream == :stdout
              file.write(data)
            else
              debug data
            end
          end
        end
      end
    end
  rescue Timeout::Error, Errno::EADDRNOTAVAIL, Errno::EADDRINUSE, Errno::EHOSTUNREACH, Errno::ECONNREFUSED, Net::SSH::AuthenticationFailed => e
    debug e.backtrace
    raise RHC::SnapshotSaveException.new "Error in trying to save snapshot. You can try to save manually by running:\n#{ssh_cmd}"
  end

  success 'done'
end
ssh_command_for_op(operation) click to toggle source
# File lib/rhc/ssh_helpers.rb, line 145
def ssh_command_for_op(operation)
  #case operation
  raise RHC::OperationNotSupportedException, "The operation #{operation} is not supported."
  #end
end
ssh_key_triple_for(key) click to toggle source

for an SSH public key specified by 'key', return a triple

type, content, comment

which is basically the space-separated list of the SSH public key content

# File lib/rhc/ssh_helpers.rb, line 407
def ssh_key_triple_for(key)
  begin
    IO.read(key).chomp.split
  rescue Errno::ENOENT => e
    raise ::RHC::KeyFileNotExistentException.new("File '#{key}' does not exist.")
  rescue Errno::EACCES => e
    raise ::RHC::KeyFileAccessDeniedException.new("Access denied to '#{key}'.")
  end
end
ssh_key_triple_for_default_key() click to toggle source
# File lib/rhc/ssh_helpers.rb, line 417
def ssh_key_triple_for_default_key
  ssh_key_triple_for(RHC::Config.ssh_pub_key_file_path)
end
ssh_keygen_fallback(path) click to toggle source

For Net::SSH versions (< 2.0.11) that does not have Net::SSH::KeyFactory.load_public_key, we drop to shell to get the key's fingerprint

# File lib/rhc/ssh_helpers.rb, line 378
def ssh_keygen_fallback(path)
  fingerprint = `ssh-keygen -lf #{path} 2>&1`.split(' ')[1]

  if $?.exitstatus != 0
    error "Unable to compute SSH public key finger print for #{path}"
  end
  fingerprint
end
ssh_ruby(host, username, command, compression=false, request_pty=false) { |channel| ... } click to toggle source

Public: Run ssh command on remote host

host - The String of the remote hostname to ssh to. username - The String username of the remote user to ssh as. command - The String command to run on the remote host. compression - Use compression in ssh, set to false if sending files. request_pty - Request for pty, set to false when pipe a file. block - Will yield this block and send the channel if provided.

Examples

ssh_ruby('myapp-t.rhcloud.com',
          '109745632b514e9590aa802ec015b074',
          'rhcsh tail -f $OPENSHIFT_LOG_DIR/*"')
# => true

Returns true on success

# File lib/rhc/ssh_helpers.rb, line 168
def ssh_ruby(host, username, command, compression=false, request_pty=false, &block)
  debug "Opening Net::SSH connection to #{host}, #{username}, #{command}"
  exit_status = 0
  options = {:compression => compression}
  options[:verbose] = :debug if debug?
  Net::SSH.start(host, username, options) do |session|
    #:nocov:
    channel = session.open_channel do |channel|
      if request_pty
        channel.request_pty do |ch, success|
          say "pty could not be obtained" unless success
        end
      end
      channel.exec(command) do |ch, success|
        channel.on_data do |ch, data|
          print data
        end
        channel.on_extended_data do |ch, type, data|
          print data
        end
        channel.on_close do |ch|
          debug "Terminating ... "
        end
        channel.on_request("exit-status") do |ch, data|
          exit_status = data.read_long
        end
        yield channel if block_given?
        channel.eof!
      end
    end
    session.loop
    #:nocov:
  end
  raise RHC::SSHCommandFailed.new(exit_status) if exit_status != 0
rescue Errno::ECONNREFUSED => e
  debug_error e
  raise RHC::SSHConnectionRefused.new(host, username)
rescue Net::SSH::AuthenticationFailed => e
  debug_error e
  raise RHC::SSHAuthenticationFailed.new(host, username)
rescue SocketError => e
  debug_error e
  raise RHC::ConnectionFailed, "The connection to #{host} failed: #{e.message}"
end
ssh_send_file_ruby(host, username, command, filename) click to toggle source

Public: Run ssh command on remote host and pipe the specified file contents to the command input

host - The String of the remote hostname to ssh to. username - The String username of the remote user to ssh as. command - The String command to run on the remote host. filename - The String path to file to send.

# File lib/rhc/ssh_helpers.rb, line 222
def ssh_send_file_ruby(host, username, command, filename)
  filename = File.expand_path(filename)
  ssh_ruby(host, username, command) do |channel|
    File.open(filename, 'rb') do |file|
      file.chunk(1024) do |chunk|
        channel.send_data chunk
      end
    end
  end
end
ssh_send_url_ruby(host, username, command, content_url) click to toggle source

Public: Run ssh command on remote host and pipe the specified url contents to the command input

host - The String of the remote hostname to ssh to. username - The String username of the remote user to ssh as. command - The String command to run on the remote host. content_url - The url with the content to pipe to command.

# File lib/rhc/ssh_helpers.rb, line 241
def ssh_send_url_ruby(host, username, command, content_url)
  content_url = URI.parse(URI.encode(content_url.to_s))
  ssh_ruby(host, username, command) do |channel|
    HTTPClient.new.get_content(content_url) do |chunk|
      channel.send_data chunk
    end
  end
end
ssh_version(cmd=discover_ssh_executable) click to toggle source

check the version of SSH that is installed

# File lib/rhc/ssh_helpers.rb, line 422
def ssh_version(cmd=discover_ssh_executable)
  `"#{cmd}" -V 2>&1`.strip
end
table_from_gears(command, groups, opts={}, &block) click to toggle source
# File lib/rhc/ssh_helpers.rb, line 139
def table_from_gears(command, groups, opts={}, &block)
  cells = run_on_gears(command, groups, {:as => :table}.merge(opts), &block)
  cells.each{ |r| r.concat(r.pop.first.split(opts[:split_cells_on])) } if !block_given? && opts[:split_cells_on]
  say table cells, opts unless options.raw
end