td 0.11.1 → 0.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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