td 0.11.1 → 0.11.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,3 @@
1
-
2
1
  module TreasureData
3
2
  module Command
4
3
 
@@ -23,9 +22,10 @@ module Command
23
22
  }
24
23
  }
25
24
 
26
- $stderr.puts "Create #{fname} with #{i} records whose time is"
27
- $stderr.puts "from #{Time.at(last_time)} to #{Time.at(t)}."
28
- $stderr.puts "Use '#{$prog} " + Config.cl_apikey_string + "table:import <db> <table> --json #{fname}' to import this file."
25
+ $stderr.print "Created #{fname} with #{i} records whose time is "
26
+ $stderr.puts "in the [#{Time.at(last_time)}, #{Time.at(t)}] range."
27
+
28
+ $stderr.puts "Use '#{$prog} " + Config.cl_options_string + "table:import <db> <table> --json #{fname}' to import this file."
29
29
  end
30
30
 
31
31
  end
@@ -1,4 +1,3 @@
1
-
2
1
  module TreasureData
3
2
  module Command
4
3
 
@@ -127,7 +126,7 @@ module Command
127
126
  rescue NotFoundError
128
127
  cmd_debug_error $!
129
128
  $stderr.puts "Schedule '#{name}' does not exist."
130
- $stderr.puts "Use '#{$prog} " + Config.cl_apikey_string + "sched:list' to show list of the schedules."
129
+ $stderr.puts "Use '#{$prog} " + Config.cl_options_string + "sched:list' to show list of the schedules."
131
130
  exit 1
132
131
  end
133
132
 
@@ -156,7 +155,7 @@ module Command
156
155
  op.on('-d', '--database DB_NAME', 'change the database') {|s|
157
156
  db_name = s
158
157
  }
159
- op.on('-r', '--result RESULT_TABLE', 'change the result table') {|s|
158
+ op.on('-r', '--result RESULT_URL', 'change the result target (see also result:create subcommand)') {|s|
160
159
  result = s
161
160
  }
162
161
  op.on('-t', '--timezone TZ', "name of the timezone.",
@@ -210,7 +209,7 @@ module Command
210
209
  rescue NotFoundError
211
210
  cmd_debug_error $!
212
211
  $stderr.puts "Schedule '#{name}' does not exist."
213
- $stderr.puts "Use '#{$prog} " + Config.cl_apikey_string + "sched:list' to show list of the schedules."
212
+ $stderr.puts "Use '#{$prog} " + Config.cl_options_string + "sched:list' to show list of the schedules."
214
213
  exit 1
215
214
  end
216
215
 
@@ -246,7 +245,7 @@ module Command
246
245
  rescue NotFoundError
247
246
  cmd_debug_error $!
248
247
  $stderr.puts "Schedule '#{name}' does not exist."
249
- $stderr.puts "Use '#{$prog} " + Config.cl_apikey_string + "sched:list' to show list of the schedules."
248
+ $stderr.puts "Use '#{$prog} " + Config.cl_options_string + "sched:list' to show list of the schedules."
250
249
  exit 1
251
250
  end
252
251
 
@@ -302,7 +301,7 @@ module Command
302
301
  rescue NotFoundError
303
302
  cmd_debug_error $!
304
303
  $stderr.puts "Schedule '#{name}' does not exist."
305
- $stderr.puts "Use '#{$prog} " + Config.cl_apikey_string + "sched:list' to show list of the schedules."
304
+ $stderr.puts "Use '#{$prog} " + Config.cl_options_string + "sched:list' to show list of the schedules."
306
305
  exit 1
307
306
  end
308
307
 
@@ -315,5 +314,5 @@ module Command
315
314
  puts cmd_render_table(rows, :fields => [:JobID, :Time], :max_width=>500, :render_format => op.render_format)
316
315
  end
317
316
 
318
- end
319
- end
317
+ end # module Command
318
+ end # module TreasureData
@@ -1,3 +1,4 @@
1
+ require 'uri'
1
2
 
2
3
  module TreasureData
3
4
  module Command
@@ -8,6 +9,26 @@ module Command
8
9
  puts Client.server_status
9
10
  end
10
11
 
12
+ def server_endpoint(op)
13
+ endpoint = op.cmd_parse
14
+
15
+ Command.validate_api_endpoint(endpoint)
16
+
17
+ if Config.cl_endpoint and endpoint != Config.endpoint
18
+ raise ParameterConfigurationError,
19
+ "You specified the API server endpoint in the command options as well (-e / --endpoint option) but it does not match the value provided to the 'server:endpoint' command. Please remove the option or ensure the endpoints URLs match each other."
20
+ end
21
+
22
+ conf = nil
23
+ begin
24
+ conf = Config.read
25
+ rescue ConfigError
26
+ conf = Config.new
27
+ end
28
+ conf["account.endpoint"] = endpoint
29
+ conf.save
30
+ end
31
+
11
32
  end
12
33
  end
13
34
 
@@ -34,7 +34,7 @@ module Command
34
34
  j = client.jobs(0, 4)
35
35
  j.each {|job|
36
36
  start = job.start_at
37
- elapsed = humanize_elapsed_time(start, job.end_at)
37
+ elapsed = Command.humanize_elapsed_time(start, job.end_at)
38
38
  jobs << {:JobID => job.job_id, :Status => job.status, :Query => job.query.to_s, :Start => (start ? start.localtime : ''), :Elapsed => elapsed, :Result => job.result_url}
39
39
  }
40
40
  x2, y2 = status_render(0, 0, "[Jobs]", jobs, :fields => [:JobID, :Status, :Start, :Elapsed, :Result, :Query])
@@ -84,6 +84,6 @@ module Command
84
84
  return movex+max_width, height
85
85
  end
86
86
 
87
- end
88
- end
87
+ end # module Command
88
+ end # module TreasureData
89
89
 
@@ -40,7 +40,6 @@ module Command
40
40
 
41
41
  db_name, table_name = op.cmd_parse
42
42
 
43
- #API.validate_database_name(db_name)
44
43
  API.validate_table_name(table_name)
45
44
 
46
45
  if HIVE_RESERVED_KEYWORDS.include?(table_name.upcase)
@@ -65,7 +64,7 @@ module Command
65
64
  rescue NotFoundError
66
65
  cmd_debug_error $!
67
66
  $stderr.puts "Database '#{db_name}' does not exist."
68
- $stderr.puts "Use '#{$prog} " + Config.cl_apikey_string + "db:create #{db_name}' to create the database."
67
+ $stderr.puts "Use '#{$prog} " + Config.cl_options_string + "db:create #{db_name}' to create the database."
69
68
  exit 1
70
69
  rescue AlreadyExistsError
71
70
  cmd_debug_error $!
@@ -111,7 +110,7 @@ module Command
111
110
  rescue NotFoundError
112
111
  cmd_debug_error $!
113
112
  $stderr.puts "Table '#{db_name}.#{table_name}' does not exist."
114
- $stderr.puts "Use '#{$prog} " + Config.cl_apikey_string + "table:list #{db_name}' to show list of the tables."
113
+ $stderr.puts "Use '#{$prog} " + Config.cl_options_string + "table:list #{db_name}' to show list of the tables."
115
114
  exit 1
116
115
  end
117
116
 
@@ -189,13 +188,13 @@ module Command
189
188
  if rows.empty?
190
189
  if db_name
191
190
  $stderr.puts "Database '#{db_name}' has no tables."
192
- $stderr.puts "Use '#{$prog} " + Config.cl_apikey_string + "table:create <db> <table>' to create a table."
191
+ $stderr.puts "Use '#{$prog} " + Config.cl_options_string + "table:create <db> <table>' to create a table."
193
192
  elsif databases.empty?
194
193
  $stderr.puts "There are no databases."
195
- $stderr.puts "Use '#{$prog} " + Config.cl_apikey_string + "db:create <db>' to create a database."
194
+ $stderr.puts "Use '#{$prog} " + Config.cl_options_string + "db:create <db>' to create a database."
196
195
  else
197
196
  $stderr.puts "There are no tables."
198
- $stderr.puts "Use '#{$prog} " + Config.cl_apikey_string + "table:create <db> <table>' to create a table."
197
+ $stderr.puts "Use '#{$prog} " + Config.cl_options_string + "table:create <db> <table>' to create a table."
199
198
  end
200
199
  end
201
200
  end
@@ -336,7 +335,7 @@ module Command
336
335
  job = table.export('s3', opts)
337
336
 
338
337
  $stderr.puts "Export job #{job.job_id} is queued."
339
- $stderr.puts "Use '#{$prog} " + Config.cl_apikey_string + "job:show #{job.job_id}' to show the status."
338
+ $stderr.puts "Use '#{$prog} " + Config.cl_options_string + "job:show #{job.job_id}' to show the status."
340
339
 
341
340
  if wait && !job.finished?
342
341
  wait_job(job)
@@ -395,7 +394,7 @@ module Command
395
394
  job = client.partial_delete(db_name, table_name, to, from, opts)
396
395
 
397
396
  $stderr.puts "Partial delete job #{job.job_id} is queued."
398
- $stderr.puts "Use '#{$prog} " + Config.cl_apikey_string + "job:show #{job.job_id}' to show the status."
397
+ $stderr.puts "Use '#{$prog} " + Config.cl_options_string + "job:show #{job.job_id}' to show the status."
399
398
 
400
399
  if wait && !job.finished?
401
400
  wait_job(job)
@@ -14,12 +14,12 @@ module Command
14
14
  start_time = Time.now
15
15
  puts "Updating 'td' from #{TOOLBELT_VERSION}..."
16
16
  if new_version = Updater.update
17
- puts "Successfully updated to #{new_version} in #{humanize_time((Time.now - start_time).to_i)}."
17
+ puts "Successfully updated to #{new_version} in #{Command.humanize_time((Time.now - start_time).to_i)}."
18
18
  else
19
19
  puts "Nothing to update."
20
20
  end
21
21
  end
22
22
 
23
- end
24
- end
23
+ end # module Command
24
+ end # module TreasureData
25
25
 
@@ -11,7 +11,7 @@ module Command
11
11
  user = users.find {|user| name == user.name }
12
12
  unless user
13
13
  $stderr.puts "User '#{name}' does not exist."
14
- $stderr.puts "Use '#{$prog} " + Config.cl_apikey_string + "user:create <name>' to create an user."
14
+ $stderr.puts "Use '#{$prog} " + Config.cl_options_string + "user:create <name>' to create an user."
15
15
  exit 1
16
16
  end
17
17
 
@@ -37,7 +37,7 @@ module Command
37
37
 
38
38
  if rows.empty?
39
39
  $stderr.puts "There are no users."
40
- $stderr.puts "Use '#{$prog} " + Config.cl_apikey_string + "user:create <name>' to create an users."
40
+ $stderr.puts "Use '#{$prog} " + Config.cl_options_string + "user:create <name>' to create an users."
41
41
  end
42
42
  end
43
43
 
@@ -120,7 +120,7 @@ module Command
120
120
  client.add_user(name, nil, email, password)
121
121
 
122
122
  $stderr.puts "User '#{name}' is created."
123
- $stderr.puts "Use '#{$prog} " + Config.cl_apikey_string + "user:apikeys #{name}' to show the API key."
123
+ $stderr.puts "Use '#{$prog} " + Config.cl_options_string + "user:apikeys #{name}' to show the API key."
124
124
  end
125
125
 
126
126
  def user_delete(op)
@@ -146,12 +146,12 @@ module Command
146
146
  client.add_apikey(name)
147
147
  rescue TreasureData::NotFoundError
148
148
  $stderr.puts "User '#{name}' does not exist."
149
- $stderr.puts "Use '#{$prog} " + Config.cl_apikey_string + "users' to show users."
149
+ $stderr.puts "Use '#{$prog} " + Config.cl_options_string + "users' to show users."
150
150
  exit 1
151
151
  end
152
152
 
153
153
  $stderr.puts "Added an API key to user '#{name}'."
154
- $stderr.puts "Use '#{$prog} " + Config.cl_apikey_string + "user:apikeys #{name}' to show the API key"
154
+ $stderr.puts "Use '#{$prog} " + Config.cl_options_string + "user:apikeys #{name}' to show the API key"
155
155
  end
156
156
 
157
157
  def user_apikey_remove(op)
@@ -163,8 +163,8 @@ module Command
163
163
  client.remove_apikey(name, key)
164
164
  rescue TreasureData::NotFoundError
165
165
  $stderr.puts "User '#{name}' or API key '#{key}' does not exist."
166
- $stderr.puts "Use '#{$prog} " + Config.cl_apikey_string + "users' to show users."
167
- $stderr.puts "Use '#{$prog} " + Config.cl_apikey_string + "user:apikeys '#{key}' to show API keys"
166
+ $stderr.puts "Use '#{$prog} " + Config.cl_options_string + "users' to show users."
167
+ $stderr.puts "Use '#{$prog} " + Config.cl_options_string + "user:apikeys '#{key}' to show API keys"
168
168
  exit 1
169
169
  end
170
170
 
@@ -13,6 +13,16 @@ end
13
13
 
14
14
 
15
15
  class Config
16
+ # class variables
17
+ @@path = ENV['TREASURE_DATA_CONFIG_PATH'] || ENV['TD_CONFIG_PATH'] || File.join(ENV['HOME'], '.td', 'td.conf')
18
+ @@apikey = ENV['TREASURE_DATA_API_KEY'] || ENV['TD_API_KEY']
19
+ @@apikey = nil if @@apikey == ""
20
+ @@cl_apikey = false # flag to indicate whether an apikey has been provided through the command-line
21
+ @@endpoint = ENV['TREASURE_DATA_API_SERVER'] || ENV['TD_API_SERVER']
22
+ @@endpoint = nil if @@endpoint == ""
23
+ @@cl_endpoint = false # flag to indicate whether an endpoint has been provided through the command-line
24
+ @@secure = true
25
+
16
26
  def initialize
17
27
  @path = nil
18
28
  @conf = {} # section.key = val
@@ -76,23 +86,18 @@ class Config
76
86
  FileUtils.mkdir_p File.dirname(@path)
77
87
  File.open(@path, "w") {|f|
78
88
  @conf.keys.map {|cate_key|
79
- cate_key.split('.',2)
80
- }.zip(@conf.values).group_by {|(section,key),val|
89
+ cate_key.split('.', 2)
90
+ }.zip(@conf.values).group_by {|(section,key), val|
81
91
  section
82
92
  }.each {|section,cate_key_vals|
83
93
  f.puts "[#{section}]"
84
- cate_key_vals.each {|(section,key),val|
94
+ cate_key_vals.each {|(section,key), val|
85
95
  f.puts " #{key} = #{val}"
86
96
  }
87
97
  }
88
98
  }
89
99
  end
90
100
 
91
- @@path = ENV['TREASURE_DATA_CONFIG_PATH'] || ENV['TD_CONFIG_PATH'] || File.join(ENV['HOME'], '.td', 'td.conf')
92
- @@apikey = ENV['TREASURE_DATA_API_KEY'] || ENV['TD_API_KEY']
93
- @@apikey = nil if @@apikey == ""
94
- @@cl_apikey = false # flag to indicate whether an apikey has been provided through the command-line
95
- @@secure = true
96
101
 
97
102
  def self.path
98
103
  @@path
@@ -102,6 +107,16 @@ class Config
102
107
  @@path = path
103
108
  end
104
109
 
110
+
111
+ def self.secure
112
+ @@secure
113
+ end
114
+
115
+ def self.secure=(secure)
116
+ @@secure = secure
117
+ end
118
+
119
+
105
120
  def self.apikey
106
121
  @@apikey || Config.read['account.apikey']
107
122
  end
@@ -118,22 +133,32 @@ class Config
118
133
  @@cl_apikey = flag
119
134
  end
120
135
 
121
- def self.cl_apikey_string
122
- if @@cl_apikey
123
- "-k #{@@apikey} "
124
- else
125
- ""
126
- end
136
+
137
+ def self.endpoint
138
+ @@endpoint || Config.read['account.endpoint']
127
139
  end
128
140
 
129
- def self.secure
130
- @@secure
141
+ def self.endpoint=(endpoint)
142
+ @@endpoint = endpoint
131
143
  end
132
144
 
133
- def self.secure=(secure)
134
- @@secure = secure
145
+ def self.cl_endpoint
146
+ @@cl_endpoint
135
147
  end
136
- end
137
148
 
149
+ def self.cl_endpoint=(flag)
150
+ @@cl_endpoint = flag
151
+ end
138
152
 
139
- end
153
+ # renders the apikey and endpoint options as a string for the helper commands
154
+ def self.cl_options_string
155
+ string = ""
156
+ string += "-k #{@@apikey}" if @@cl_apikey
157
+ string += " " unless string.empty?
158
+ string += "-e #{@@endpoint}" if @@cl_endpoint
159
+ string += " " unless string.empty?
160
+ string
161
+ end
162
+
163
+ end # class Config
164
+ end # module TreasureData
@@ -4,429 +4,442 @@ require "shellwords"
4
4
  require "zip/zip"
5
5
 
6
6
  module TreasureData
7
- module Updater
8
- #
9
- # Toolbelt upgrade
10
- #
11
-
12
- def self.raise_error(message)
13
- # TODO: Replace better Exception class
14
- raise RuntimeError.new(message)
15
- end
16
-
17
- # copied from TreasureData::Helpers to avoid load issue.
18
- def self.home_directory
19
- on_windows? ? ENV['USERPROFILE'].gsub("\\","/") : ENV['HOME']
20
- end
21
-
22
- def self.on_windows?
23
- RUBY_PLATFORM =~ /mswin32|mingw32/
24
- end
25
-
26
- def self.on_mac?
27
- RUBY_PLATFORM =~ /-darwin\d/
28
- end
29
-
30
- def self.updating_lock_path
31
- File.join(home_directory, ".td", "updating")
32
- end
33
-
34
- def self.installed_client_path
35
- File.expand_path("../../../../../..", __FILE__)
36
- end
37
-
38
- def self.updated_client_path
39
- File.join(home_directory, ".td", "updated")
40
- end
41
-
42
- def self.latest_local_version
43
- installed_version = client_version_from_path(installed_client_path)
44
- updated_version = client_version_from_path(updated_client_path)
45
- if compare_versions(updated_version, installed_version) > 0
46
- updated_version
47
- else
48
- installed_version
7
+ module Updater
8
+ #
9
+ # Toolbelt upgrade
10
+ #
11
+
12
+ def self.raise_error(message)
13
+ # TODO: Replace better Exception class
14
+ raise RuntimeError.new(message)
15
+ end
16
+
17
+ # copied from TreasureData::Helpers to avoid load issue.
18
+ def self.home_directory
19
+ on_windows? ? ENV['USERPROFILE'].gsub("\\","/") : ENV['HOME']
20
+ end
21
+
22
+ def self.on_windows?
23
+ RUBY_PLATFORM =~ /mswin32|mingw32/
24
+ end
25
+
26
+ def self.on_mac?
27
+ RUBY_PLATFORM =~ /-darwin\d/
28
+ end
29
+
30
+ def self.updating_lock_path
31
+ File.join(home_directory, ".td", "updating")
32
+ end
33
+
34
+ def self.installed_client_path
35
+ File.expand_path("../../../../../..", __FILE__)
36
+ end
37
+
38
+ def self.updated_client_path
39
+ File.join(home_directory, ".td", "updated")
40
+ end
41
+
42
+ def self.latest_local_version
43
+ installed_version = client_version_from_path(installed_client_path)
44
+ updated_version = client_version_from_path(updated_client_path)
45
+ if compare_versions(updated_version, installed_version) > 0
46
+ updated_version
47
+ else
48
+ installed_version
49
+ end
50
+ end
51
+
52
+ def self.get_client_version_file(path)
53
+ td_gems = Dir[File.join(path, "vendor/gems/td-*")]
54
+ td_gems.each { |td_gem|
55
+ if td_gem =~ /#{"#{Regexp.escape(path)}\/vendor\/gems\/td-\\d*.\\d*.\\d*"}/
56
+ return File.join(td_gem, "/lib/td/version.rb")
49
57
  end
50
- end
51
-
52
- def self.get_client_version_file(path)
53
- td_gems = Dir[File.join(path, "vendor/gems/td-*")]
54
- td_gems.each { |td_gem|
55
- if td_gem =~ /#{"#{Regexp.escape(path)}\/vendor\/gems\/td-\\d*.\\d*.\\d*"}/
56
- return File.join(td_gem, "/lib/td/version.rb")
57
- end
58
- }
59
- nil
60
- end
61
-
62
- def self.client_version_from_path(path)
63
- if version_file = get_client_version_file(path)
64
- File.read(version_file).match(/TOOLBELT_VERSION = '([^']+)'/)[1]
65
- else
66
- '0.0.0'
58
+ }
59
+ nil
60
+ end
61
+
62
+ def self.client_version_from_path(path)
63
+ if version_file = get_client_version_file(path)
64
+ File.read(version_file).match(/TOOLBELT_VERSION = '([^']+)'/)[1]
65
+ else
66
+ '0.0.0'
67
+ end
68
+ end
69
+
70
+ def self.disable(message)
71
+ @disable = message
72
+ end
73
+
74
+ def self.disable?
75
+ !@disable.nil?
76
+ end
77
+
78
+ def self.disable_message
79
+ @disable
80
+ end
81
+
82
+ def self.wait_for_lock(path, wait_for = 5, check_every = 0.5)
83
+ start = Time.now.to_i
84
+ while File.exists?(path)
85
+ sleep check_every
86
+ if (Time.now.to_i - start) > wait_for
87
+ raise_error "Unable to acquire update lock"
67
88
  end
68
89
  end
69
-
70
- def self.disable(message)
71
- @disable = message
72
- end
73
-
74
- def self.disable?
75
- !@disable.nil?
76
- end
77
-
78
- def self.disable_message
79
- @disable
80
- end
81
-
82
- def self.wait_for_lock(path, wait_for = 5, check_every = 0.5)
83
- start = Time.now.to_i
84
- while File.exists?(path)
85
- sleep check_every
86
- if (Time.now.to_i - start) > wait_for
87
- raise_error "Unable to acquire update lock"
88
- end
89
- end
90
- begin
91
- FileUtils.touch(path)
92
- ret = yield
93
- ensure
94
- FileUtils.rm_f(path)
95
- end
96
- ret
97
- end
98
-
99
- def self.package_category
100
- case
101
- when on_windows?
102
- 'exe'
103
- when on_mac?
104
- 'pkg'
105
- else
106
- raise_error "Non supported environment"
107
- end
108
- end
109
-
110
- def self.fetch(uri)
111
- require 'net/http'
112
- require 'openssl'
113
-
114
- # open-uri can't treat 'http -> https' redirection and
115
- # Net::HTTP.get_response can't get response from HTTPS endpoint.
116
- # So we use following code to avoid above issues.
117
- u = URI(uri)
118
- response =
119
- if u.scheme == 'https'
120
- http = Net::HTTP.new(u.host, u.port)
121
- http.use_ssl = true
122
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
123
- http.request(Net::HTTP::Get.new(u.path))
124
- else
125
- Net::HTTP.get_response(u)
126
- end
127
-
128
- case response
129
- when Net::HTTPSuccess then response.body
130
- when Net::HTTPRedirection then fetch(response['Location'])
90
+ begin
91
+ FileUtils.touch(path)
92
+ ret = yield
93
+ ensure
94
+ FileUtils.rm_f(path)
95
+ end
96
+ ret
97
+ end
98
+
99
+ def self.package_category
100
+ case
101
+ when on_windows?
102
+ 'exe'
103
+ when on_mac?
104
+ 'pkg'
105
+ else
106
+ raise_error "Non supported environment"
107
+ end
108
+ end
109
+
110
+ def self.fetch(url)
111
+ require 'net/http'
112
+ require 'openssl'
113
+
114
+ http_class = Command.get_http_class
115
+
116
+ # open-uri can't treat 'http -> https' redirection and
117
+ # Net::HTTP.get_response can't get response from HTTPS endpoint.
118
+ # So we use following code to avoid these issues.
119
+ uri = URI(url)
120
+ response =
121
+ if uri.scheme == 'https' and ENV['HTTP_PROXY'].nil?
122
+ # NOTE: SSL is force off for communications over proxy
123
+ http = http_class.new(uri.host, uri.port)
124
+ http.use_ssl = true
125
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
126
+ http.request(Net::HTTP::Get.new(uri.path))
131
127
  else
132
- raise "An error occurred when fetching from '#{uri}'."
133
- response.error!
128
+ http_class.get_response(uri)
134
129
  end
135
- end
136
130
 
137
- def self.endpoint_root
138
- ENV['TD_TOOLBELT_UPDATE_ROOT'] || "http://toolbelt.treasuredata.com"
131
+ case response
132
+ when Net::HTTPSuccess then response.body
133
+ when Net::HTTPRedirection then fetch(response['Location'])
134
+ else
135
+ raise "An error occurred when fetching from '#{url}'."
136
+ response.error!
139
137
  end
138
+ end
140
139
 
141
- def self.version_endpoint
142
- "#{endpoint_root}/version.#{package_category}"
143
- end
144
-
145
- def self.update_package_endpoint
146
- "#{endpoint_root}/td-update-#{package_category}.zip"
147
- end
140
+ def self.endpoint_root
141
+ ENV['TD_TOOLBELT_UPDATE_ROOT'] || "http://toolbelt.treasuredata.com"
142
+ end
148
143
 
149
- def self.update(autoupdate = false)
150
- wait_for_lock(updating_lock_path, 5) do
151
- require "td"
152
- require 'open-uri'
153
- require "tmpdir"
154
- require "zip/zip"
155
-
156
- latest_version = fetch(version_endpoint)
157
-
158
- if compare_versions(latest_version, latest_local_version) > 0
159
- Dir.mktmpdir do |download_dir|
160
-
161
- # initialize the progress indicator
162
- base_msg = "Downloading updated toolbelt package"
163
- print base_msg + " " * 10
164
- start_time = last_time = Time.new.to_i
165
-
166
- # downloading the update compressed file
167
- File.open("#{download_dir}/td-update.zip", "wb") do |file|
168
- endpoint = update_package_endpoint
169
- puts "\npackage '#{endpoint}'... " unless ENV['TD_TOOLBELT_DEBUG'].nil?
170
- stream_fetch(endpoint, file) {
171
-
172
- # progress indicator
173
- if (time = Time.now.to_i) - last_time > 2
174
- msg = "\r" + base_msg + ": #{time - start_time}s elapsed"
175
- # TODO parse 'time - start_time' with humanize_time from common.rb
176
- # once this method is not static
177
- print msg + " " * 10
178
- last_time = time
179
- end
180
- }
181
- end
144
+ def self.version_endpoint
145
+ "#{endpoint_root}/version.#{package_category}"
146
+ end
182
147
 
183
- # clear progress indicator with this final message
184
- puts "\r" + base_msg + "...done" + " " * 20
148
+ def self.update_package_endpoint
149
+ "#{endpoint_root}/td-update-#{package_category}.zip"
150
+ end
185
151
 
186
- print "Unpacking updated toolbelt package..."
187
- Zip::ZipFile.open("#{download_dir}/td-update.zip") do |zip|
188
- zip.each do |entry|
189
- target = File.join(download_dir, entry.to_s)
190
- FileUtils.mkdir_p(File.dirname(target))
191
- zip.extract(entry, target) { true }
192
- end
152
+ def self.update(autoupdate = false)
153
+ wait_for_lock(updating_lock_path, 5) do
154
+ require "td"
155
+ require 'open-uri'
156
+ require "tmpdir"
157
+ require "zip/zip"
158
+
159
+ latest_version = fetch(version_endpoint)
160
+
161
+ if compare_versions(latest_version, latest_local_version) > 0
162
+ Dir.mktmpdir do |download_dir|
163
+
164
+ indicator = Command::TimeBasedDownloadProgressIndicator.new(
165
+ "Downloading updated toolbelt package", Time.new.to_i, 2)
166
+ # downloading the update compressed file
167
+ File.open("#{download_dir}/td-update.zip", "wb") do |file|
168
+ endpoint = update_package_endpoint
169
+ puts "\npackage '#{endpoint}'... " unless ENV['TD_TOOLBELT_DEBUG'].nil?
170
+ stream_fetch(endpoint, file) {
171
+ indicator.update
172
+ }
173
+ end
174
+ indicator.finish
175
+
176
+ print "Unpacking updated toolbelt package..."
177
+ Zip::ZipFile.open("#{download_dir}/td-update.zip") do |zip|
178
+ zip.each do |entry|
179
+ target = File.join(download_dir, entry.to_s)
180
+ FileUtils.mkdir_p(File.dirname(target))
181
+ zip.extract(entry, target) { true }
193
182
  end
194
- print "done\n"
183
+ end
184
+ print "done\n"
195
185
 
196
- FileUtils.rm "#{download_dir}/td-update.zip"
186
+ FileUtils.rm "#{download_dir}/td-update.zip"
197
187
 
198
- old_version = latest_local_version
199
- new_version = client_version_from_path(download_dir)
188
+ old_version = latest_local_version
189
+ new_version = client_version_from_path(download_dir)
200
190
 
201
- if compare_versions(new_version, old_version) < 0 && !autoupdate
202
- raise_error "Installed version (#{old_version}) is newer than the latest available update (#{new_version})"
203
- end
191
+ if compare_versions(new_version, old_version) < 0 && !autoupdate
192
+ raise_error "Installed version (#{old_version}) is newer than the latest available update (#{new_version})"
193
+ end
204
194
 
205
- FileUtils.rm_rf updated_client_path
206
- FileUtils.mkdir_p File.dirname(updated_client_path)
207
- FileUtils.cp_r(download_dir, updated_client_path)
195
+ FileUtils.rm_rf updated_client_path
196
+ FileUtils.mkdir_p File.dirname(updated_client_path)
197
+ FileUtils.cp_r(download_dir, updated_client_path)
208
198
 
209
- new_version
210
- end
211
- else
212
- false # already up to date
199
+ new_version
213
200
  end
201
+ else
202
+ false # already up to date
214
203
  end
215
- ensure
216
- FileUtils.rm_f(updating_lock_path)
217
204
  end
205
+ ensure
206
+ FileUtils.rm_f(updating_lock_path)
207
+ end
218
208
 
219
- def self.compare_versions(first_version, second_version)
220
- first_version.split('.').map { |part| Integer(part) rescue part } <=> second_version.split('.').map { |part| Integer(part) rescue part }
221
- end
209
+ def self.compare_versions(first_version, second_version)
210
+ first_version.split('.').map { |part| Integer(part) rescue part } <=> second_version.split('.').map { |part| Integer(part) rescue part }
211
+ end
222
212
 
223
- def self.inject_libpath
224
- old_version = client_version_from_path(installed_client_path)
225
- new_version = client_version_from_path(updated_client_path)
213
+ def self.inject_libpath
214
+ old_version = client_version_from_path(installed_client_path)
215
+ new_version = client_version_from_path(updated_client_path)
226
216
 
227
- if compare_versions(new_version, old_version) > 0
228
- vendored_gems = Dir[File.join(updated_client_path, "vendor", "gems", "*")]
229
- vendored_gems.each do |vendored_gem|
230
- $:.unshift File.join(vendored_gem, "lib")
231
- end
232
- load('td/updater.rb') # reload updated updater
217
+ if compare_versions(new_version, old_version) > 0
218
+ vendored_gems = Dir[File.join(updated_client_path, "vendor", "gems", "*")]
219
+ vendored_gems.each do |vendored_gem|
220
+ $:.unshift File.join(vendored_gem, "lib")
233
221
  end
222
+ load('td/updater.rb') # reload updated updater
223
+ end
234
224
 
235
- # check every hour if the toolbelt can be updated.
236
- # => If so, update in the background
237
- if File.exists?(last_toolbelt_autoupdate_timestamp)
238
- return if (Time.now.to_i - File.mtime(last_toolbelt_autoupdate_timestamp).to_i) < 60 * 60 * 1 # every 1 hours
225
+ # check every hour if the toolbelt can be updated.
226
+ # => If so, update in the background
227
+ if File.exists?(last_toolbelt_autoupdate_timestamp)
228
+ return if (Time.now.to_i - File.mtime(last_toolbelt_autoupdate_timestamp).to_i) < 60 * 60 * 1 # every 1 hours
229
+ end
230
+ log_path = File.join(home_directory, '.td', 'autoupdate.log')
231
+ FileUtils.mkdir_p File.dirname(log_path)
232
+ td_binary = File.expand_path($0)
233
+ pid = if defined?(RUBY_VERSION) and RUBY_VERSION =~ /^1\.8\.\d+/
234
+ fork do
235
+ exec("#{Shellwords.escape(td_binary)} update &> #{Shellwords.escape(log_path)} 2>&1")
239
236
  end
240
- log_path = File.join(home_directory, '.td', 'autoupdate.log')
241
- FileUtils.mkdir_p File.dirname(log_path)
242
- td_binary = File.expand_path($0)
243
- pid = if defined?(RUBY_VERSION) and RUBY_VERSION =~ /^1\.8\.\d+/
244
- fork do
245
- exec("#{Shellwords.escape(td_binary)} update &> #{Shellwords.escape(log_path)} 2>&1")
237
+ else
238
+ log_file = File.open(log_path, "w")
239
+ spawn(td_binary, 'update', :err => log_file, :out => log_file)
240
+ end
241
+ Process.detach(pid)
242
+ FileUtils.mkdir_p File.dirname(last_toolbelt_autoupdate_timestamp)
243
+ FileUtils.touch last_toolbelt_autoupdate_timestamp
244
+ end
245
+
246
+ def self.last_toolbelt_autoupdate_timestamp
247
+ File.join(home_directory, ".td", "autoupdate.last")
248
+ end
249
+
250
+ #
251
+ # td-import.jar upgrade
252
+ #
253
+
254
+ # locate the root of the td package which is 3 folders up from the location of this file
255
+ def jarfile_dest_path
256
+ File.join(Updater.home_directory, ".td", "java")
257
+ end
258
+
259
+ private
260
+ def self.stream_fetch(url, binfile, &progress)
261
+ require 'net/http'
262
+
263
+ uri = URI(url)
264
+ http_class = Command.get_http_class
265
+ http = http_class.new(uri.host, uri.port)
266
+
267
+ # NOTE: keep SSL off when using a proxy
268
+ if uri.scheme == 'https' and ENV['HTTP_PROXY'].nil?
269
+ http.use_ssl = true
270
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
271
+ end
272
+
273
+ http.request_get(uri.path) {|response|
274
+ if response.class == Net::HTTPOK
275
+ # print a . every tick_period seconds
276
+ response.read_body do |chunk|
277
+ binfile.write chunk
278
+ progress.call unless progress.nil?
246
279
  end
280
+ return true
281
+ elsif response.class == Net::HTTPFound || \
282
+ response.class == Net::HTTPRedirection
283
+ puts "redirect '#{url}' to '#{response['Location']}'... " unless ENV['TD_TOOLBELT_DEBUG'].nil?
284
+ return stream_fetch(response['Location'], binfile, &progress)
247
285
  else
248
- log_file = File.open(log_path, "w")
249
- spawn(td_binary, 'update', :err => log_file, :out => log_file)
286
+ raise "An error occurred when fetching from '#{uri}' " +
287
+ "(#{response.class.to_s}: #{response.message})."
288
+ return false
250
289
  end
251
- Process.detach(pid)
252
- FileUtils.mkdir_p File.dirname(last_toolbelt_autoupdate_timestamp)
253
- FileUtils.touch last_toolbelt_autoupdate_timestamp
254
- end
290
+ }
291
+ end
255
292
 
256
- def self.last_toolbelt_autoupdate_timestamp
257
- File.join(home_directory, ".td", "autoupdate.last")
258
- end
293
+ private
294
+ def jar_update(hourly = false)
295
+ require 'rexml/document'
296
+ require 'open-uri'
297
+ require 'fileutils'
259
298
 
260
- #
261
- # td-import.jar upgrade
262
- #
299
+ maven_repo = "http://maven.treasure-data.com/com/treasure_data/td-import"
263
300
 
264
- # locate the root of the td package which is 3 folders up from the location of this file
265
- def jarfile_dest_path
266
- File.join(Updater.home_directory, ".td", "java")
301
+ begin
302
+ xml = Updater.fetch("#{maven_repo}/maven-metadata.xml")
303
+ rescue Exception => exc
304
+ raise Command::UpdateError,
305
+ "There was a problem accessing the remote XML resource '#{maven_repo}/maven-metadata.xml' " +
306
+ "(#{exc.class.to_s}: #{exc.message})"
267
307
  end
268
-
269
- private
270
- def self.stream_fetch(url, binfile, &progress)
271
- require 'net/http'
272
-
273
- uri = URI(url)
274
- http = Net::HTTP.new(uri.host, uri.port)
275
- if uri.scheme == 'https'
276
- http.use_ssl = true
277
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
278
- end
279
- http.start
280
- request = Net::HTTP::Get.new(uri.to_s)
281
- http.request request do |response|
282
- if response.class == Net::HTTPOK
283
- # print a . every tick_period seconds
284
- response.read_body do |chunk|
285
- binfile.write chunk
286
- progress.call #unless progress.nil?
287
- end
288
- return true
289
- elsif response.class == Net::HTTPFound || \
290
- response.class == Net::HTTPRedirection
291
- puts "redirect '#{url}' to '#{response['Location']}'... " unless ENV['TD_TOOLBELT_DEBUG'].nil?
292
- return stream_fetch(response['Location'], binfile, &progress)
293
- else
294
- raise "An error occurred when fetching from '#{uri}'."
295
- return false
296
- end
297
- end
308
+ if xml.nil? || xml.empty?
309
+ raise Command::UpdateError,
310
+ "The remote XML resource '#{maven_repo}/maven-metadata.xml' returned an empty file."
298
311
  end
299
312
 
300
- private
301
- def jar_update(hourly = false)
302
- maven_repo = "http://maven.treasure-data.com/com/treasure_data/td-import"
313
+ # read version and update date from the xml file
314
+ doc = REXML::Document.new(xml)
315
+ updated = Time.strptime(REXML::XPath.match(doc, '/metadata/versioning/lastUpdated').first.text, "%Y%m%d%H%M%S")
316
+ version = REXML::XPath.match(doc, '/metadata/versioning/release').first.text
303
317
 
304
- require 'rexml/document'
305
- require 'open-uri'
306
- require 'fileutils'
307
-
308
- doc = REXML::Document.new(open("#{maven_repo}/maven-metadata.xml") { |f| f.read })
309
- updated = Time.strptime(REXML::XPath.match(doc, '/metadata/versioning/lastUpdated').first.text, "%Y%m%d%H%M%S")
310
- version = REXML::XPath.match(doc, '/metadata/versioning/release').first.text
311
-
312
- # Convert into UTF to compare time correctly
313
- updated = (updated + updated.gmt_offset).utc unless updated.gmt?
314
- last_updated = existent_jar_updated_time
315
-
316
- if updated > last_updated
317
- FileUtils.mkdir_p(jarfile_dest_path) unless File.exists?(jarfile_dest_path)
318
- Dir.chdir jarfile_dest_path
319
-
320
- File.open('VERSION', 'w') { |f| f.print "#{version} via import:jar_update" }
321
- File.open('td-import-java.version', 'w') { |f| f.print "#{version} #{updated}" }
322
-
323
- # initialize the progress indicator
324
- base_msg = "Updating td-import.jar"
325
- start_time = last_time = Time.new.to_i
326
- print base_msg + " " * 10
327
-
328
- binfile = File.open 'td-import.jar.new', 'wb'
329
- status = Updater.stream_fetch("#{maven_repo}/#{version}/td-import-#{version}-jar-with-dependencies.jar", binfile) {
330
-
331
- # progress indicator
332
- if (time = Time.now.to_i) - last_time > 2
333
- msg = "\r" + base_msg + ": #{humanize_time(time - start_time)} elapsed"
334
- # TODO parse 'time - start_time' with humanize_time from common.rb
335
- # once this method is not static
336
- print msg + " " * 10
337
- last_time = time
338
- end
339
- }
340
- binfile.close
318
+ # Convert into UTF to compare time correctly
319
+ updated = (updated + updated.gmt_offset).utc unless updated.gmt?
320
+ last_updated = existent_jar_updated_time
341
321
 
342
- # clear progress indicator with this final message
343
- puts "\r" + base_msg + "...done" + " " * 20
322
+ if updated > last_updated
323
+ FileUtils.mkdir_p(jarfile_dest_path) unless File.exists?(jarfile_dest_path)
324
+ Dir.chdir jarfile_dest_path
344
325
 
345
- if status
346
- puts "Installed td-import.jar v#{version} in '#{jarfile_dest_path}'.\n"
347
- File.rename 'td-import.jar.new', 'td-import.jar'
326
+ File.open('VERSION', 'w') {|f|
327
+ if hourly
328
+ f.print "#{version} via hourly jar auto-update"
348
329
  else
349
- puts "Update of td-import.jar failed." unless ENV['TD_TOOLBELT_DEBUG'].nil?
350
- File.delete 'td-import.jar.new' if File.exists? 'td-import.jar.new'
330
+ f.print "#{version} via import:jar_update command"
351
331
  end
352
- else
353
- puts 'Installed td-import.jar is already at the latest version.' unless hourly
354
- end
355
- end
356
-
357
- def check_n_update_jar(hourly = false)
358
- if hourly && \
359
- File.exists?(last_jar_autoupdate_timestamp) && \
360
- (Time.now - File.mtime(last_jar_autoupdate_timestamp)).to_i < (60 * 60 * 1) # every hour
361
- return
362
- end
363
- jar_update(hourly)
364
- FileUtils.touch File.join(jarfile_dest_path, "td-import-java.version")
365
- end
366
-
367
- private
368
- def last_jar_autoupdate_timestamp
369
- File.join(jarfile_dest_path, "td-import-java.version")
370
- end
371
-
372
- private
373
- def existent_jar_updated_time
374
- files = find_files("td-import-java.version", [jarfile_dest_path])
375
- if files.empty?
376
- return Time.at(0)
377
- end
378
- content = File.open(files.first).read
379
- index = content.index(' ')
380
- time = nil
381
- if index.nil?
382
- time = Time.at(0).utc
383
- else
384
- time = Time.parse(content[index+1..-1].strip).utc
385
- end
386
- time
387
- end
332
+ }
333
+ File.open('td-import-java.version', 'w') {|f|
334
+ f.print "#{version} #{updated}"
335
+ }
388
336
 
389
- #
390
- # Helpers
391
- #
392
- def find_files(glob, locations)
393
- files = []
394
- locations.each {|loc|
395
- files = Dir.glob("#{loc}/#{glob}")
396
- break unless files.empty?
337
+ indicator = Command::TimeBasedDownloadProgressIndicator.new(
338
+ "Updating td-import.jar", Time.new.to_i, 2)
339
+ binfile = File.open 'td-import.jar.new', 'wb'
340
+ status = Updater.stream_fetch("#{maven_repo}/#{version}/td-import-#{version}-jar-with-dependencies.jar", binfile) {
341
+ indicator.update
397
342
  }
398
- files
399
- end
343
+ binfile.close
344
+ indicator.finish()
400
345
 
401
- def find_version_file
402
- version = find_files('VERSION', [jarfile_dest_path])
403
- if version.empty?
404
- $stderr.puts "Cannot find VERSION file in '#{jarfile_dest_path}'."
405
- exit 10
346
+ if status
347
+ puts "Installed td-import.jar v#{version} in '#{jarfile_dest_path}'.\n"
348
+ File.rename 'td-import.jar.new', 'td-import.jar'
349
+ else
350
+ puts "Update of td-import.jar failed." unless ENV['TD_TOOLBELT_DEBUG'].nil?
351
+ File.delete 'td-import.jar.new' if File.exists? 'td-import.jar.new'
406
352
  end
407
- version.first
408
- end
409
-
410
- def find_td_import_jar
411
- jar = find_files('td-import.jar', [jarfile_dest_path])
412
- if jar.empty?
413
- $stderr.puts "Cannot find td-import.jar in '#{jarfile_dest_path}'."
414
- exit 10
353
+ else
354
+ puts 'Installed td-import.jar is already at the latest version.' unless hourly
355
+ end
356
+ end
357
+
358
+ def check_n_update_jar(hourly = false)
359
+ if hourly
360
+ if !ENV['TD_TOOLBELT_JAR_UPDATE'].nil?
361
+ # also validates the TD_TOOLBELT_JAR_UPDATE environment variable value
362
+ if ENV['TD_TOOLBELT_JAR_UPDATE'] == "0"
363
+ puts "Warning: Bulk Import JAR auto-update disabled by TD_TOOLBELT_JAR_UPDATE=0"
364
+ return
365
+ elsif ENV['TD_TOOLBELT_JAR_UPDATE'] != "1"
366
+ raise UpdateError,
367
+ "Invalid value for TD_TOOLBELT_JAR_UPDATE environment variable. Only 0 and 1 are allowed."
368
+ end
415
369
  end
416
- jar.first
417
- end
418
-
419
- def find_logging_property
420
- installed_path = File.join(File.expand_path('../..', File.dirname(__FILE__)), 'java')
421
370
 
422
- config = find_files("logging.properties", [installed_path])
423
- if config.empty?
424
- puts "Cannot find 'logging.properties' file in '#{installed_path}'." unless ENV['TD_TOOLBELT_DEBUG'].nil?
425
- []
426
- else
427
- config.first
371
+ if File.exists?(last_jar_autoupdate_timestamp) && \
372
+ (Time.now - File.mtime(last_jar_autoupdate_timestamp)).to_i < (60 * 60 * 1) # every hour
373
+ return
428
374
  end
429
375
  end
430
-
431
- end # end of module Updater
432
- end # end of module TreasureData
376
+ jar_update(hourly)
377
+ FileUtils.touch last_jar_autoupdate_timestamp
378
+ end
379
+
380
+ private
381
+ def last_jar_autoupdate_timestamp
382
+ File.join(jarfile_dest_path, "td-import-java.version")
383
+ end
384
+
385
+ private
386
+ def existent_jar_updated_time
387
+ files = find_files("td-import-java.version", [jarfile_dest_path])
388
+ if files.empty?
389
+ return Time.at(0)
390
+ end
391
+ content = File.read(files.first)
392
+ index = content.index(' ')
393
+ time = nil
394
+ if index.nil?
395
+ time = Time.at(0).utc
396
+ else
397
+ time = Time.parse(content[index+1..-1].strip).utc
398
+ end
399
+ time
400
+ end
401
+
402
+ #
403
+ # Helpers
404
+ #
405
+ def find_files(glob, locations)
406
+ files = []
407
+ locations.each {|loc|
408
+ files = Dir.glob("#{loc}/#{glob}")
409
+ break unless files.empty?
410
+ }
411
+ files
412
+ end
413
+
414
+ def find_version_file
415
+ version = find_files('VERSION', [jarfile_dest_path])
416
+ if version.empty?
417
+ $stderr.puts "Cannot find VERSION file in '#{jarfile_dest_path}'."
418
+ exit 10
419
+ end
420
+ version.first
421
+ end
422
+
423
+ def find_td_import_jar
424
+ jar = find_files('td-import.jar', [jarfile_dest_path])
425
+ if jar.empty?
426
+ $stderr.puts "Cannot find td-import.jar in '#{jarfile_dest_path}'."
427
+ exit 10
428
+ end
429
+ jar.first
430
+ end
431
+
432
+ def find_logging_property
433
+ installed_path = File.join(File.expand_path('../..', File.dirname(__FILE__)), 'java')
434
+
435
+ config = find_files("logging.properties", [installed_path])
436
+ if config.empty?
437
+ puts "Cannot find 'logging.properties' file in '#{installed_path}'." unless ENV['TD_TOOLBELT_DEBUG'].nil?
438
+ []
439
+ else
440
+ config.first
441
+ end
442
+ end
443
+
444
+ end # module Updater
445
+ end # module TreasureData