Module: TreasureData::Updater::ModuleDefinition

Included in:
TreasureData::Updater
Defined in:
lib/td/updater.rb

Overview

Toolbelt upgrade

Instance Method Summary collapse

Instance Method Details

#client_version_from_path(path) ⇒ Object



63
64
65
66
67
68
69
# File 'lib/td/updater.rb', line 63

def client_version_from_path(path)
  if version_file = get_client_version_file(path)
    File.read(version_file).match(/TOOLBELT_VERSION = '([^']+)'/)[1]
  else
    '0.0.0'
  end
end

#compare_versions(first_version, second_version) ⇒ Object



210
211
212
# File 'lib/td/updater.rb', line 210

def compare_versions(first_version, second_version)
  first_version.split('.').map { |part| Integer(part) rescue part } <=> second_version.split('.').map { |part| Integer(part) rescue part }
end

#disable(message) ⇒ Object



71
72
73
# File 'lib/td/updater.rb', line 71

def disable(message)
  @disable = message
end

#disable?Boolean

Returns:

  • (Boolean)


75
76
77
# File 'lib/td/updater.rb', line 75

def disable?
  !@disable.nil?
end

#disable_messageObject



79
80
81
# File 'lib/td/updater.rb', line 79

def disable_message
  @disable
end

#endpoint_rootObject



140
141
142
# File 'lib/td/updater.rb', line 140

def endpoint_root
  ENV['TD_TOOLBELT_UPDATE_ROOT'] || "http://toolbelt.treasuredata.com"
end

#fetch(url) ⇒ Object



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
# File 'lib/td/updater.rb', line 111

def fetch(url)
  require 'net/http'
  require 'openssl'

  http_class = Command.get_http_class

  # open-uri can't treat 'http -> https' redirection and
  # Net::HTTP.get_response can't get response from HTTPS endpoint.
  # So we use following code to avoid these issues.
  uri = URI(url)
  response =
    if uri.scheme == 'https'
      http = http_class.new(uri.host, uri.port)
      http.use_ssl = true
      http.verify_mode = OpenSSL::SSL::VERIFY_NONE
      http.request(Net::HTTP::Get.new(uri.path))
    else
      http_class.get_response(uri)
    end

  case response
  when Net::HTTPSuccess then response.body
  when Net::HTTPRedirection then fetch(response['Location'])
  else
    raise_error "An error occurred when fetching from '#{url}'."
    response.error!
  end
end

#get_client_version_file(path) ⇒ Object



53
54
55
56
57
58
59
60
61
# File 'lib/td/updater.rb', line 53

def get_client_version_file(path)
  td_gems = Dir[File.join(path, "vendor/gems/td-*")]
  td_gems.each { |td_gem|
    if /[\/\\]td-\d+.\d+.\d+\z/ =~ td_gem
      return File.join(td_gem, "/lib/td/version.rb")
    end
  }
  nil
end

#home_directoryObject

copied from TreasureData::Helpers to avoid load issue.



19
20
21
# File 'lib/td/updater.rb', line 19

def home_directory
  on_windows? ? ENV['USERPROFILE'].gsub("\\","/") : ENV['HOME']
end

#inject_libpathObject



214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/td/updater.rb', line 214

def inject_libpath
  old_version = client_version_from_path(installed_client_path)
  new_version = client_version_from_path(updated_client_path)

  if compare_versions(new_version, old_version) > 0
    vendored_gems = Dir[File.join(updated_client_path, "vendor", "gems", "*")]
    vendored_gems.each do |vendored_gem|
      $:.unshift File.join(vendored_gem, "lib")
    end
    load('td/updater.rb') # reload updated updater
  end
end

#installed_client_pathObject



35
36
37
# File 'lib/td/updater.rb', line 35

def installed_client_path
  File.expand_path("../../../../../..", __FILE__)
end

#jarfile_dest_pathObject

locate the root of the td package which is 3 folders up from the location of this file



232
233
234
# File 'lib/td/updater.rb', line 232

def jarfile_dest_path
  File.join(home_directory, ".td", "java")
end

#latest_local_versionObject



43
44
45
46
47
48
49
50
51
# File 'lib/td/updater.rb', line 43

def latest_local_version
  installed_version = client_version_from_path(installed_client_path)
  updated_version = client_version_from_path(updated_client_path)
  if compare_versions(updated_version, installed_version) > 0
    updated_version
  else
    installed_version
  end
end

#on_mac?Boolean

Returns:

  • (Boolean)


27
28
29
# File 'lib/td/updater.rb', line 27

def on_mac?
  RUBY_PLATFORM =~ /-darwin\d/
end

#on_windows?Boolean

Returns:

  • (Boolean)


23
24
25
# File 'lib/td/updater.rb', line 23

def on_windows?
  RUBY_PLATFORM =~ /mswin32|mingw32|mingw-ucrt/
end

#package_categoryObject



100
101
102
103
104
105
106
107
108
109
# File 'lib/td/updater.rb', line 100

def package_category
  case
  when on_windows?
    'exe'
  when on_mac?
    'pkg'
  else
    raise_error "Environment not supported"
  end
end

#raise_error(message) ⇒ Object



13
14
15
16
# File 'lib/td/updater.rb', line 13

def raise_error(message)
  # TODO: Replace better Exception class
  raise Command::UpdateError, message
end

#stream_fetch(url, binfile, &progress) ⇒ Object



236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
# File 'lib/td/updater.rb', line 236

def stream_fetch(url, binfile, &progress)
  require 'net/http'
  require 'openssl'

  uri = URI(url)
  http_class = Command.get_http_class
  http = http_class.new(uri.host, uri.port)

  if uri.scheme == 'https'
    http.use_ssl = true
    http.verify_mode = OpenSSL::SSL::VERIFY_NONE
  end

  http.request_get(uri.path + (uri.query ? '?' + uri.query : '')) {|response|
    if response.class == Net::HTTPOK
      # $stdout.print a . every tick_period seconds
      response.read_body do |chunk|
        binfile.write chunk
        progress.call unless progress.nil?
      end
      return true
    elsif response.is_a?(Net::HTTPRedirection)
      unless ENV['TD_TOOLBELT_DEBUG'].nil?
        $stdout.puts "redirect '#{url}' to '#{response['Location']}'... "
      end
      return stream_fetch(response['Location'], binfile, &progress)
    else
      raise_error "An error occurred when fetching from '#{uri}' " +
        "(#{response.class.to_s}: #{response.message})."
      return false
    end
  }
end

#update(autoupdate = false) ⇒ Object



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/td/updater.rb', line 152

def update(autoupdate = false)
  FileUtils.mkdir_p File.dirname(updating_lock_path)
  wait_for_lock(updating_lock_path, 5) do
    require "td"
    require 'open-uri'
    require "tmpdir"
    require "zip/zip"

    latest_version = fetch(version_endpoint)

    if compare_versions(latest_version, latest_local_version) > 0
      Dir.mktmpdir do |download_dir|

        indicator = Command::TimeBasedDownloadProgressIndicator.new(
          "Downloading updated toolbelt package", Time.new.to_i, 2)
        # downloading the update compressed file
        File.open("#{download_dir}/td-update.zip", "wb") do |file|
          endpoint = update_package_endpoint
          $stdout.puts "\npackage '#{endpoint}'... " unless ENV['TD_TOOLBELT_DEBUG'].nil?
          stream_fetch(endpoint, file) {
            indicator.update
          }
        end
        indicator.finish

        $stdout.print "Unpacking updated toolbelt package..."
        Zip::ZipFile.open("#{download_dir}/td-update.zip") do |zip|
          zip.each do |entry|
            target = File.join(download_dir, entry.to_s)
            FileUtils.mkdir_p(File.dirname(target))
            zip.extract(entry, target) { true }
          end
        end
        $stdout.print " done\n"

        FileUtils.rm "#{download_dir}/td-update.zip"

        old_version = latest_local_version
        new_version = client_version_from_path(download_dir)

        if compare_versions(new_version, old_version) < 0 && !autoupdate
          raise_error "Installed version (#{old_version}) is newer than the latest available update (#{new_version})"
        end

        FileUtils.rm_rf updated_client_path
        FileUtils.mkdir_p File.dirname(updated_client_path)
        FileUtils.cp_r(download_dir, updated_client_path)

        new_version
      end
    else
      false # already up to date
    end
  end
ensure
  FileUtils.rm_f(updating_lock_path)
end

#update_package_endpointObject



148
149
150
# File 'lib/td/updater.rb', line 148

def update_package_endpoint
  "#{endpoint_root}/td-update-#{package_category}-0-17.zip"
end

#updated_client_pathObject



39
40
41
# File 'lib/td/updater.rb', line 39

def updated_client_path
  File.join(home_directory, ".td", "updated")
end

#updating_lock_pathObject



31
32
33
# File 'lib/td/updater.rb', line 31

def updating_lock_path
  File.join(home_directory, ".td", "updating")
end

#version_endpointObject



144
145
146
# File 'lib/td/updater.rb', line 144

def version_endpoint
  "#{endpoint_root}/latest_version_0_17.#{package_category}"
end

#wait_for_lock(path, wait_for = 5, check_every = 0.5) ⇒ Object



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/td/updater.rb', line 83

def wait_for_lock(path, wait_for = 5, check_every = 0.5)
  start = Time.now.to_i
  while File.exist?(path)
    sleep check_every
    if (Time.now.to_i - start) > wait_for
      raise_error "Unable to acquire update lock"
    end
  end
  begin
    FileUtils.touch(path)
    ret = yield
  ensure
    FileUtils.rm_f(path)
  end
  ret
end