Module: TreasureData::Updater

Included in:
Command
Defined in:
lib/td/updater.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.client_version_from_path(path) ⇒ Object



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

def self.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



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

def self.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



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

def self.disable(message)
  @disable = message
end

.disable?Boolean

Returns:

  • (Boolean)


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

def self.disable?
  !@disable.nil?
end

.disable_messageObject



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

def self.disable_message
  @disable
end

.endpoint_rootObject



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

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

.fetch(url) ⇒ Object



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

def self.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' and ENV['HTTP_PROXY'].nil?
      # NOTE: SSL is force off for communications over proxy
      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 "An error occurred when fetching from '#{url}'."
    response.error!
  end
end

.get_client_version_file(path) ⇒ Object



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

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

.home_directoryObject

copied from TreasureData::Helpers to avoid load issue.



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

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

.inject_libpathObject



213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
# File 'lib/td/updater.rb', line 213

def self.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

  # check every hour if the toolbelt can be updated.
  # => If so, update in the background
  if File.exists?(last_toolbelt_autoupdate_timestamp)
    return if (Time.now.to_i - File.mtime(last_toolbelt_autoupdate_timestamp).to_i) < 60 * 60 * 1 # every 1 hours
  end
  log_path = File.join(home_directory, '.td', 'autoupdate.log')
  FileUtils.mkdir_p File.dirname(log_path)
  td_binary = File.expand_path($0)
  pid = if defined?(RUBY_VERSION) and RUBY_VERSION =~ /^1\.8\.\d+/
    fork do
      exec("#{Shellwords.escape(td_binary)} update &> #{Shellwords.escape(log_path)} 2>&1")
    end
  else
    log_file = File.open(log_path, "w")
    spawn(td_binary, 'update', :err => log_file, :out => log_file)
  end
  Process.detach(pid)
  FileUtils.mkdir_p File.dirname(last_toolbelt_autoupdate_timestamp)
  FileUtils.touch last_toolbelt_autoupdate_timestamp
end

.installed_client_pathObject



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

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

.last_toolbelt_autoupdate_timestampObject



246
247
248
# File 'lib/td/updater.rb', line 246

def self.last_toolbelt_autoupdate_timestamp
  File.join(home_directory, ".td", "autoupdate.last")
end

.latest_local_versionObject



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

def self.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)


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

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

.on_windows?Boolean

Returns:

  • (Boolean)


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

def self.on_windows?
  RUBY_PLATFORM =~ /mswin32|mingw32/
end

.package_categoryObject



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

def self.package_category
  case
  when on_windows?
    'exe'
  when on_mac?
    'pkg'
  else
    raise_error "Non supported environment"
  end
end

.raise_error(message) ⇒ Object

Toolbelt upgrade

Raises:

  • (RuntimeError)


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

def self.raise_error(message)
  # TODO: Replace better Exception class
  raise RuntimeError.new(message)
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
# File 'lib/td/updater.rb', line 152

def self.update(autoupdate = false)
  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
          puts "\npackage '#{endpoint}'... " unless ENV['TD_TOOLBELT_DEBUG'].nil?
          stream_fetch(endpoint, file) {
            indicator.update
          }
        end
        indicator.finish

        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
        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 self.update_package_endpoint
  "#{endpoint_root}/td-update-#{package_category}.zip"
end

.updated_client_pathObject



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

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

.updating_lock_pathObject



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

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

.version_endpointObject



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

def self.version_endpoint
  "#{endpoint_root}/version.#{package_category}"
end

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



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

def self.wait_for_lock(path, wait_for = 5, check_every = 0.5)
  start = Time.now.to_i
  while File.exists?(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

Instance Method Details

#jarfile_dest_pathObject

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



255
256
257
# File 'lib/td/updater.rb', line 255

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