gabetax/dbcp

View on GitHub
lib/dbcp/execution_hosts/ssh_execution_host.rb

Summary

Maintainability
A
1 hr
Test Coverage
require 'net/ssh'
require 'net/sftp'
require 'uri'

module Dbcp
  class SshExecutionHost < ExecutionHost

    class << self
      def new_from_uri(uri_string)
        uri = URI.parse uri_string
        raise URI::InvalidURIError.new "SSH URI must be in form 'ssh://username@example.com/path/to_application_root'. We received: '#{uri_string}'." unless uri.user && uri.host
        new({
          host:     uri.host,
          port:     uri.port,
          username: uri.user,
          path:     uri.path
        })
      end
    end

    values do
      attribute :host
      attribute :port, Fixnum
      attribute :username
      attribute :path
    end

    def remote?
      true
    end

    def execute(command)
      # http://stackoverflow.com/questions/3386233/how-to-get-exit-status-with-rubys-netssh-library
      stdout_data = ""
      stderr_data = ""
      exitstatus  = nil
      exit_signal = nil

      Net::SSH.start host, username do |ssh|
        ssh.open_channel do |channel|
          channel.exec command do |ch, success|
            unless success
              raise ExecutionError.new "Exection over SSH to failed for: (ssh.channel.exec)"
            end
            channel.on_data do |ch,data|
              stdout_data+=data
            end

            channel.on_extended_data do |ch,type,data|
              stderr_data+=data
            end

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

            channel.on_request("exit-signal") do |ch, data|
              exit_signal = data.read_long
            end
          end
        end
        ssh.loop
      end

      raise ExecutionError.new "Execution failed with exit code #{$?.exitstatus}. Command was: #{command}" if exitstatus > 0
    end

    # Omitting destination_path will return file contents as a string
    def download(source_path, destination_path = nil)
      Net::SFTP.start host, username do |ssh|
        return ssh.download! source_path, destination_path
      end
    end

    def upload(source_path, destination_path)
      Net::SFTP.start host, username do |ssh|
        return ssh.upload! source_path, destination_path
      end
    end

    def remote_database(database_yaml_path, environment_name)
      Database.build remote_yaml(database_yaml_path)[environment_name]
    end

    def remote_yaml(remote_yaml_path)
      YAML.load download("#{path}/#{remote_yaml_path}")
    end
  end
end