class SftpWrapper::OpenSSH

The wrapper for OpenSSH's sftp command.

Constants

DEFAULT_COMMAND_TIMEOUT

Default value of command timeout.

DEFAULT_OPEN_TIMEOUT

Default value of open timeout.

DEFAULT_READ_TIMEOUT

Default value of read timeout.

DEFAULT_SSH_CONFIG

Default value of ssh config file path.

DEFAULT_SSH_OPTIONS

Default value of ssh options

PASSWORD_PROMPT_RE

pattern of password prompt.

SFTP_PROMPT

sftp prompt.

SFTP_PROMPT_RE

pattern of sftp prompt.

Attributes

command_timeout[R]
debug[R]
host[R]
open_timeout[R]
password[R]
port[R]
read_timeout[R]
ssh_config[R]
ssh_options[R]
username[R]

Public Class Methods

new(host, port, username, password, ssh_options: {}, ssh_config: DEFAULT_SSH_CONFIG, open_timeout: DEFAULT_OPEN_TIMEOUT, read_timeout: DEFAULT_READ_TIMEOUT, command_timeout: DEFAULT_COMMAND_TIMEOUT, debug: false) click to toggle source

Initialize SFTP wrapper.

@param host [String] host address of SFTP server @param port [Integer] port number of SFTP server @param username [String] user name of SFTP server @param password [String] password of SFTP server @param ssh_options [Hash] SSH options (set as -o options) @param ssh_config [String] path of SSH config file (set as -F option unless nil) @param open_timeout [Integer, Float] @param read_timeout [Integer, Float] @param command_timeout [Integer, Float] @param debug [Boolean]

rubocop:disable Metrics/ParameterLists

# File lib/sftp_wrapper/open_ssh.rb, line 62
def initialize(host, port, username, password,
               ssh_options: {},
               ssh_config: DEFAULT_SSH_CONFIG,
               open_timeout: DEFAULT_OPEN_TIMEOUT,
               read_timeout: DEFAULT_READ_TIMEOUT,
               command_timeout: DEFAULT_COMMAND_TIMEOUT,
               debug: false)
  @host = host
  @port = port
  @username = username
  @password = password
  @ssh_options = DEFAULT_SSH_OPTIONS.merge(ssh_options)
  @ssh_config = ssh_config
  @open_timeout = open_timeout
  @read_timeout = read_timeout
  @command_timeout = command_timeout
  @debug = debug
end

Public Instance Methods

download(source, destination) click to toggle source

Get remote file.

@param source [String] source file path @param destination [String] destination path @raise [SftpWrapper::Errors::ConnectionError] @raise [SftpWrapper::Errors::AuthenticationFailure] @raise [SftpWrapper::Errors::TimeoutError] @raise [SftpWrapper::Errors::CommandError]

# File lib/sftp_wrapper/open_ssh.rb, line 91
def download(source, destination)
  execute(%W[get #{source} #{destination}].shelljoin)
end
upload(source, destination) click to toggle source

Put local file.

@param source [String] source file path @param destination [String] destination path @raise [SftpWrapper::Errors::ConnectionError] @raise [SftpWrapper::Errors::AuthenticationFailure] @raise [SftpWrapper::Errors::TimeoutError] @raise [SftpWrapper::Errors::CommandError]

# File lib/sftp_wrapper/open_ssh.rb, line 104
def upload(source, destination)
  execute(%W[put #{source} #{destination}].shelljoin)
end

Private Instance Methods

build_cli_args() click to toggle source
# File lib/sftp_wrapper/open_ssh.rb, line 146
def build_cli_args
  args = []
  args << '-q'
  args += %W[-F #{ssh_config}] if ssh_config
  args += %W[-P #{port} -o User=#{username}]
  args += ssh_options.map { |k, v| %W[-o #{k}=#{v}] }.flatten
  args
end
execute(command) click to toggle source

Execute sftp command.

@param command [String] SFTP command @raise [SftpWrapper::Errors::ConnectionError] @raise [SftpWrapper::Errors::AuthenticationFailure] @raise [SftpWrapper::Errors::TimeoutError] @raise [SftpWrapper::Errors::CommandError]

# File lib/sftp_wrapper/open_ssh.rb, line 118
def execute(command) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
  cli = %w[sftp] + build_cli_args + [host]
  PTY.getpty(cli.shelljoin) do |r, w, _pid|
    w.sync = true

    unless safe_expect(r, PASSWORD_PROMPT_RE, open_timeout)
      raise SftpWrapper::Errors::ConnectionError, 'connection error.'
    end

    w.puts(password)

    unless safe_expect(r, SFTP_PROMPT_RE, read_timeout)
      raise SftpWrapper::Errors::AuthenticationFailure, 'authentication failure'
    end

    w.puts(command)

    res = safe_expect(r, SFTP_PROMPT_RE, command_timeout)
    raise SftpWrapper::Errors::TimeoutError, 'command timed out' unless res

    skip = command.bytesize + 2
    error_message = res[0].byteslice(skip..-1).chomp.sub(/#{SFTP_PROMPT}\z/, '').chomp
    raise SftpWrapper::Errors::CommandError, error_message unless error_message.empty?

    w.puts('quit')
  end
end
safe_expect(io, pattern, timeout) click to toggle source

@see github.com/ruby/ruby/blob/13b692200dba1056fa9033f2c64c43453f6d6a98/ext/pty/pty.c#L716-L723

# File lib/sftp_wrapper/open_ssh.rb, line 156
def safe_expect(io, pattern, timeout)
  expect_verbose = $expect_verbose # rubocop:disable Style/GlobalVars
  $expect_verbose = debug # rubocop:disable Style/GlobalVars
  io.expect(pattern, timeout)
rescue Errno::EIO
  nil
ensure
  $expect_verbose = expect_verbose # rubocop:disable Style/GlobalVars
end