Class: RightScale::Downloader

Inherits:
Object
  • Object
show all
Defined in:
lib/instance/downloader.rb

Overview

Abstract download capabilities

Constant Summary collapse

DEFAULT_MAX_DOWNLOAD_RETRIES =
10

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(retry_period = 10, use_backoff = true, max_retry_period = 5 * 60) ⇒ Downloader

Initialize downloader with given retry period When using backoff algorithm the retry period specifies the initial value, the value then gets incremented after each retry exponentially until it reaches the specified maximum retry period

Parameters

retry_period(Integer)

Retry period in seconds - defaults to 10 seconds

use_backoff(Boolean)

Whether download should use a backoff algorithm to space retries - defaults to true

max_retry_period(Integer)

Maximum retry period in seconds, only meaningful when using backoff algorithm - defaults to 5 minutes



41
42
43
44
45
46
47
# File 'lib/instance/downloader.rb', line 41

def initialize(retry_period = 10, use_backoff = true, max_retry_period = 5 * 60)
  @retry_period = retry_period
  @use_backoff = use_backoff
  @max_retry_period = max_retry_period if use_backoff
  platform = RightScale::Platform
  @found_curl = platform.filesystem.has_executable_in_path('curl')
end

Instance Attribute Details

#max_retry_periodObject

(Integer) Maximum retry period in seconds, only meaningful when using backoff algorithm



56
57
58
# File 'lib/instance/downloader.rb', line 56

def max_retry_period
  @max_retry_period
end

#retry_periodObject

(Integer) Retry period in seconds



50
51
52
# File 'lib/instance/downloader.rb', line 50

def retry_period
  @retry_period
end

#sizeObject (readonly)

(Integer) Size in bytes of last successful download (nil if none)



59
60
61
# File 'lib/instance/downloader.rb', line 59

def size
  @size
end

#speedObject (readonly)

(Integer) Speed in bytes/seconds of last successful download (nil if none)



62
63
64
# File 'lib/instance/downloader.rb', line 62

def speed
  @speed
end

#use_backoffObject

(Boolean) Whether download should use a backoff algorithm to space retries



53
54
55
# File 'lib/instance/downloader.rb', line 53

def use_backoff
  @use_backoff
end

Instance Method Details

#detailsObject

Message summarizing last successful download details

Return

details(String)

Message with last url, download size and speed



154
155
156
# File 'lib/instance/downloader.rb', line 154

def details
  "Downloaded #{sanitize_uri(@last_url)} (#{ scale(size.to_i).join(' ') }) at #{ scale(speed.to_i).join(' ') }/s"
end

#download(url, dest, username = nil, password = nil, max_retries = DEFAULT_MAX_DOWNLOAD_RETRIES) ⇒ Object

Download file synchronously and report on success, download size and download speed. Use successful, size and speed to query about last download. If last download failed, use error to retrieve error message. Requires ‘curl’ to be available on PATH.

Parameters

url(String)

URL to downloaded file

dest(String)

Path where file should be saved on disk

username(String)

Optional HTTP basic authentication username

password(String)

Optional HTTP basic authentication password

max_retries(Integer)

Maximum number of retries - defaults to DEFAULT_MAX_DOWNLOAD_RETRIES

Block

Call (optional) passed in block after each unsuccessful download attempt. Block must accept one argument corresponding to the last returned http code.

Return

true

Download was successful

false

Download failed



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/instance/downloader.rb', line 101

def download(url, dest, username=nil, password=nil, max_retries=DEFAULT_MAX_DOWNLOAD_RETRIES)
  @errors = []
  retry_count = error_code = 0
  success = false
  reset_wait_time_span

  @errors << 'curl is not installed' unless @found_curl

  @errors << "destination file '#{dest}' is a directory" if File.directory?(dest)
  begin
    FileUtils.mkdir_p(File.dirname(dest)) unless File.directory?(File.dirname(dest))
  rescue Exception => e
    @errors << e.message
  end

  return false unless @errors.empty?

  # format curl command and redirect stderr away.
  #
  # note: ensure we use double-quotes (") to surround arguments on command
  # line because single-quotes (') are literals in windows.
  platform = RightScale::Platform
  user_opt = username && password ? "--user \"#{username}:#{password}\"" : ""
  dest = platform.filesystem.long_path_to_short_path(dest)
  cmd = "curl --fail --silent --show-error --insecure --location --connect-timeout 300 --max-time 3600 --write-out \"%{http_code} %{size_download} %{speed_download}\" #{user_opt} --output \"#{dest}\" \"#{url}\""
  cmd = platform.shell.format_redirect_stderr(cmd)
  begin
    out = `#{cmd}`
    out = out.split
    success = $?.success? && out.size == 3
    if success
      @size = out[1].to_i
      @speed = out[2].to_i
      @last_url = url
      return true
    else
      retry_count += 1
      error_code = out[0].to_i
      yield error_code if block_given?
      sleep wait_time_span
    end
  end until success || retry_count >= max_retries
  unless success
    @errors << "#{retry_count} download attempts failed, last HTTP response code was #{error_code}"
    return false
  end
  true
end

#errorObject

Error message associated with last failure (nil if none)

Return

error(String)

Error message

nil

No error occured during last download



69
70
71
# File 'lib/instance/downloader.rb', line 69

def error
  error = (@errors.nil? ||  @errors.empty?) ? nil : @errors.join("\n")
end

#successful?Boolean

Was last download successful?

Return

true

If last download was successful or there was no download yet

false

Otherwise

Returns:

  • (Boolean)


78
79
80
# File 'lib/instance/downloader.rb', line 78

def successful?
  error.nil?
end