class Fig::Protocol::SFTP

File transfers via SFTP

Constants

NET_SFTP_ISSUE_27_SIZE

See github.com/net-ssh/net-sftp/issues/27

Public Class Methods

new() click to toggle source
# File lib/fig/protocol/sftp.rb, line 19
def initialize()
  initialize_netrc
end

Public Instance Methods

download(uri, path, prompt_for_login) click to toggle source

Returns whether the file was not downloaded because the file already exists and is already up-to-date.

# File lib/fig/protocol/sftp.rb, line 77
def download(uri, path, prompt_for_login)
  sftp_run(uri, prompt_for_login) do
    |connection|

    begin
      # *sigh* Always call #stat!(), even if the local file does not exist
      # because #download!() throws Strings and not proper exception objects
      # when the remote path does not exist.
      stat = connection.stat!(uri.path)

      if (
            ::File.exist?(path)                         \
        &&  stat.mtime.to_f <= ::File.mtime(path).to_f  \
        &&  stat.size != ::File.size(path)
      )
        Fig::Logging.debug "#{path} is up to date."
        return false
      end

      if stat.size >= NET_SFTP_ISSUE_27_SIZE
        Fig::Logging.warn(
          "#{uri} is #{stat.size} bytes in size, which is likely to trigger a Net::SFTP bug (https://github.com/net-ssh/net-sftp/issues/27). Fig may hang. If so, you will need to kill the process and switch file transfer protocols or figure out a way to repackage with smaller files."
        )
      end

      log_download uri, path
      connection.download! uri.path, path

      return true
    rescue Net::SFTP::StatusException => error
      if error.code == Net::SFTP::Constants::StatusCodes::FX_NO_SUCH_FILE
        raise Fig::FileNotFoundError.new(error.message, uri)
      end
      raise error
    end
  end

  return
end
download_list(uri) click to toggle source
# File lib/fig/protocol/sftp.rb, line 23
def download_list(uri)
  package_versions = []

  sftp_run(uri, :prompt_for_login) do
    |connection|

    connection.dir.foreach uri.path do
      |package_directory|

      if package_directory.directory?
        package_name = package_directory.name

        if package_name =~ Fig::PackageDescriptor::COMPONENT_PATTERN
          connection.dir.foreach "#{uri.path}/#{package_name}" do
            |version_directory|

            if version_directory.directory?
              version_name = version_directory.name

              if version_name =~ Fig::PackageDescriptor::COMPONENT_PATTERN
                package_versions << "#{package_name}/#{version_name}"
              end
            end
          end
        end
      end
    end
  end

  return package_versions
end
path_up_to_date?(uri, path, prompt_for_login) click to toggle source

Determine whether we need to update something. Returns nil to indicate “don't know”.

# File lib/fig/protocol/sftp.rb, line 57
def path_up_to_date?(uri, path, prompt_for_login)
  sftp_run(uri, prompt_for_login) do
    |connection|

    stat_attributes = connection.stat!(uri.path)
    if stat_attributes.size != ::File.size(path)
      return false
    end

    return stat_attributes.mtime.to_f <= ::File.mtime(path).to_f
  end

  return nil
end
upload(local_file, uri) click to toggle source
# File lib/fig/protocol/sftp.rb, line 117
def upload(local_file, uri)
  sftp_run(uri, :prompt_for_login) do
    |connection|

    ensure_directory_exists connection, ::File.dirname(uri.path)
    connection.upload! local_file, uri.path
  end

  return
end

Private Instance Methods

ensure_directory_exists(connection, path) click to toggle source
# File lib/fig/protocol/sftp.rb, line 155
def ensure_directory_exists(connection, path)
  begin
    connection.lstat!(path)
    return
  rescue Net::SFTP::StatusException => error
    if error.code != Net::SFTP::Constants::StatusCodes::FX_NO_SUCH_FILE
      raise Fig::NetworkError.new(
        "Could not stat #{path}: #{response.message} (#{response.code})"
      )
    end
  end

  if path == '/'
    raise Fig::NetworkError.new 'Root path does not exist.'
  end

  ensure_directory_exists connection, ::File.dirname(path)

  connection.mkdir! path

  return
end
sftp_run(uri, prompt_for_login, &block) click to toggle source
# File lib/fig/protocol/sftp.rb, line 130
def sftp_run(uri, prompt_for_login, &block)
  host = uri.host

  authentication = get_authentication_for host, prompt_for_login
  if ! authentication
    raise Fig::NetworkError.new "No authentication information for #{host}."
  end

  begin
    options = {:password => authentication.password}
    port = uri.port
    if port
      options[:port] = port
    end

    Net::SFTP.start(host, authentication.username, options, &block)
  rescue Net::SSH::Exception => error
    raise Fig::NetworkError.new error.message
  rescue Net::SFTP::Exception => error
    raise Fig::NetworkError.new error.message
  end

  return
end