td-client 0.8.85 → 0.9.0dev1
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.
- checksums.yaml +4 -4
- data/lib/td/client.rb +8 -16
- data/lib/td/client/api.rb +46 -62
- data/lib/td/client/api/bulk_import.rb +2 -1
- data/lib/td/client/api/bulk_load.rb +3 -3
- data/lib/td/client/api/export.rb +0 -12
- data/lib/td/client/api/import.rb +2 -3
- data/lib/td/client/api/job.rb +71 -145
- data/lib/td/client/api/schedule.rb +1 -1
- data/lib/td/client/api_error.rb +0 -5
- data/lib/td/client/model.rb +28 -91
- data/lib/td/client/version.rb +1 -1
- data/spec/spec_helper.rb +5 -5
- data/spec/td/client/account_api_spec.rb +5 -5
- data/spec/td/client/api_spec.rb +51 -69
- data/spec/td/client/api_ssl_connection_spec.rb +1 -1
- data/spec/td/client/bulk_import_spec.rb +29 -28
- data/spec/td/client/bulk_load_spec.rb +35 -60
- data/spec/td/client/db_api_spec.rb +1 -1
- data/spec/td/client/export_api_spec.rb +1 -11
- data/spec/td/client/import_api_spec.rb +10 -85
- data/spec/td/client/job_api_spec.rb +61 -567
- data/spec/td/client/model_job_spec.rb +10 -27
- data/spec/td/client/model_schedule_spec.rb +2 -2
- data/spec/td/client/partial_delete_api_spec.rb +1 -1
- data/spec/td/client/result_api_spec.rb +3 -3
- data/spec/td/client/sched_api_spec.rb +4 -12
- data/spec/td/client/server_status_api_spec.rb +2 -2
- data/spec/td/client/spec_resources.rb +0 -1
- data/spec/td/client/table_api_spec.rb +14 -14
- data/spec/td/client/user_api_spec.rb +12 -12
- data/spec/td/client_sched_spec.rb +6 -31
- data/spec/td/client_spec.rb +0 -1
- metadata +97 -42
- data/spec/td/client/api_error_spec.rb +0 -77
- data/spec/td/client/model_schema_spec.rb +0 -134
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fbabfd3204808160bd8fd4f5a7cd3c5c48eb1b11
|
4
|
+
data.tar.gz: bd585ffc36231bc199d78b36f0e67a8205d6939a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d88e942c5fa4cdb4aa793419d6213ef80b048449e9383eef3cced73e30dc4f8b0a4f8ba39e2ea13ad5ce4fafb462b13f5e48acd213c87e3e9cf52b02a3f9470c
|
7
|
+
data.tar.gz: 356cfac4a8a32e3c574b0f5928ad80bf75e040952fc438c632b7ef3e3e7310950253103870c62277b75632299febb670cf3baaade3aecb67f45123010587d13c
|
data/lib/td/client.rb
CHANGED
@@ -182,10 +182,10 @@ class Client
|
|
182
182
|
results = @api.list_jobs(from, to, status, conditions)
|
183
183
|
results.map {|job_id, type, status, query, start_at, end_at, cpu_time,
|
184
184
|
result_size, result_url, priority, retry_limit, org, db,
|
185
|
-
duration
|
185
|
+
duration|
|
186
186
|
Job.new(self, job_id, type, query, status, nil, nil, start_at, end_at, cpu_time,
|
187
187
|
result_size, nil, result_url, nil, priority, retry_limit, org, db,
|
188
|
-
duration
|
188
|
+
duration)
|
189
189
|
}
|
190
190
|
end
|
191
191
|
|
@@ -194,9 +194,9 @@ class Client
|
|
194
194
|
def job(job_id)
|
195
195
|
job_id = job_id.to_s
|
196
196
|
type, query, status, url, debug, start_at, end_at, cpu_time,
|
197
|
-
result_size, result_url, hive_result_schema, priority, retry_limit, org, db
|
197
|
+
result_size, result_url, hive_result_schema, priority, retry_limit, org, db = @api.show_job(job_id)
|
198
198
|
Job.new(self, job_id, type, query, status, url, debug, start_at, end_at, cpu_time,
|
199
|
-
result_size, nil, result_url, hive_result_schema, priority, retry_limit, org, db
|
199
|
+
result_size, nil, result_url, hive_result_schema, priority, retry_limit, org, db)
|
200
200
|
end
|
201
201
|
|
202
202
|
# @param [String] job_id
|
@@ -254,14 +254,6 @@ class Client
|
|
254
254
|
Job.new(self, job_id, :export, nil)
|
255
255
|
end
|
256
256
|
|
257
|
-
# @param [String] target_job_id
|
258
|
-
# @param [Hash] opts
|
259
|
-
# @return [Job]
|
260
|
-
def result_export(target_job_id, opts={})
|
261
|
-
job_id = @api.result_export(target_job_id, opts)
|
262
|
-
Job.new(self, job_id, :result_export, nil)
|
263
|
-
end
|
264
|
-
|
265
257
|
# @param [String] db_name
|
266
258
|
# @param [String] table_name
|
267
259
|
# @param [Fixnum] to
|
@@ -364,7 +356,7 @@ class Client
|
|
364
356
|
raise ArgumentError, "'cron' option is required" unless opts[:cron] || opts['cron']
|
365
357
|
raise ArgumentError, "'query' option is required" unless opts[:query] || opts['query']
|
366
358
|
start = @api.create_schedule(name, opts)
|
367
|
-
return
|
359
|
+
return Time.parse(start)
|
368
360
|
end
|
369
361
|
|
370
362
|
# @param [String] name
|
@@ -582,9 +574,9 @@ class Client
|
|
582
574
|
@api.bulk_load_show(name)
|
583
575
|
end
|
584
576
|
|
585
|
-
# name: String,
|
586
|
-
def bulk_load_update(name,
|
587
|
-
@api.bulk_load_update(name,
|
577
|
+
# name: String, job: BulkLoad -> BulkLoad
|
578
|
+
def bulk_load_update(name, job)
|
579
|
+
@api.bulk_load_update(name, job)
|
588
580
|
end
|
589
581
|
|
590
582
|
# name: String -> BulkLoad
|
data/lib/td/client/api.rb
CHANGED
@@ -36,19 +36,16 @@ class API
|
|
36
36
|
include API::Table
|
37
37
|
include API::User
|
38
38
|
|
39
|
-
DEFAULT_ENDPOINT = 'api.
|
40
|
-
DEFAULT_IMPORT_ENDPOINT = 'api-import.
|
39
|
+
DEFAULT_ENDPOINT = 'api.treasure-data.com'
|
40
|
+
DEFAULT_IMPORT_ENDPOINT = 'api-import.treasure-data.com'
|
41
41
|
|
42
|
-
|
43
|
-
|
44
|
-
NEW_DEFAULT_IMPORT_ENDPOINT = DEFAULT_IMPORT_ENDPOINT
|
45
|
-
OLD_ENDPOINT = 'api.treasure-data.com'
|
42
|
+
NEW_DEFAULT_ENDPOINT = 'api.treasuredata.com'
|
43
|
+
NEW_DEFAULT_IMPORT_ENDPOINT = 'api-import.treasuredata.com'
|
46
44
|
|
47
45
|
class IncompleteError < APIError; end
|
48
46
|
|
49
47
|
# @param [String] apikey
|
50
48
|
# @param [Hash] opts
|
51
|
-
# for backward compatibility
|
52
49
|
def initialize(apikey, opts={})
|
53
50
|
require 'json'
|
54
51
|
require 'time'
|
@@ -95,18 +92,12 @@ class API
|
|
95
92
|
# generic URI
|
96
93
|
@host, @port = endpoint.split(':', 2)
|
97
94
|
@port = @port.to_i
|
98
|
-
if opts[:ssl]
|
99
|
-
# for backward compatibility, old endpoint specified without ssl option, use http
|
100
|
-
#
|
101
|
-
# opts[:ssl] would be nil if user doesn't specify ssl options,
|
102
|
-
# but connecting to https is the new default behavior (since 0.9)
|
103
|
-
# so check ssl option by `if opts[:ssl] === false` instead of `if opts[:ssl]`
|
104
|
-
# that means if user desire to use http, give `:ssl => false` for initializer such as API.new("APIKEY", :ssl => false)
|
105
|
-
@port = 80 if @port == 0
|
106
|
-
@ssl = false
|
107
|
-
else
|
95
|
+
if opts[:ssl]
|
108
96
|
@port = 443 if @port == 0
|
109
97
|
@ssl = true
|
98
|
+
else
|
99
|
+
@port = 80 if @port == 0
|
100
|
+
@ssl = false
|
110
101
|
end
|
111
102
|
@base_path = ''
|
112
103
|
end
|
@@ -149,27 +140,15 @@ class API
|
|
149
140
|
end
|
150
141
|
|
151
142
|
name = name.to_s
|
152
|
-
if
|
153
|
-
|
154
|
-
raise ParameterValidationError,
|
155
|
-
"#{target.capitalize} name must be between #{min_len} and #{max_len} characters long. Got #{name.length} " +
|
156
|
-
(name.length == 1 ? "character" : "characters") + "."
|
157
|
-
end
|
158
|
-
else
|
159
|
-
if min_len == 1
|
160
|
-
if name.empty?
|
161
|
-
raise ParameterValidationError,
|
143
|
+
if name.empty?
|
144
|
+
raise ParameterValidationError,
|
162
145
|
"Empty #{target} name is not allowed"
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
"#{target.capitalize} name must be longer than #{min_len} characters. Got #{name.length} " +
|
146
|
+
end
|
147
|
+
if name.length < min_len || name.length > max_len
|
148
|
+
raise ParameterValidationError,
|
149
|
+
"#{target.capitalize} name must be between #{min_len} and #{max_len} characters long. Got #{name.length} " +
|
168
150
|
(name.length == 1 ? "character" : "characters") + "."
|
169
|
-
end
|
170
|
-
end
|
171
151
|
end
|
172
|
-
|
173
152
|
unless name =~ /^([a-z0-9_]+)$/
|
174
153
|
raise ParameterValidationError,
|
175
154
|
"#{target.capitalize} name must only consist of lower-case alpha-numeric characters and '_'."
|
@@ -195,18 +174,7 @@ class API
|
|
195
174
|
|
196
175
|
# @param [String] name
|
197
176
|
def self.validate_column_name(name)
|
198
|
-
|
199
|
-
name = name.to_s
|
200
|
-
if name.empty?
|
201
|
-
raise ParameterValidationError,
|
202
|
-
"Empty #{target} name is not allowed"
|
203
|
-
end
|
204
|
-
name
|
205
|
-
end
|
206
|
-
|
207
|
-
# @param [String] name
|
208
|
-
def self.validate_sql_alias_name(name)
|
209
|
-
validate_name("sql_alias", 1, nil, name)
|
177
|
+
validate_name("column", 1, 255, name)
|
210
178
|
end
|
211
179
|
|
212
180
|
# @param [String] name
|
@@ -231,6 +199,25 @@ class API
|
|
231
199
|
normalize_database_name(name)
|
232
200
|
end
|
233
201
|
|
202
|
+
# TODO support array types
|
203
|
+
# @param [String] name
|
204
|
+
def self.normalize_type_name(name)
|
205
|
+
case name
|
206
|
+
when /int/i, /integer/i
|
207
|
+
"int"
|
208
|
+
when /long/i, /bigint/i
|
209
|
+
"long"
|
210
|
+
when /string/i
|
211
|
+
"string"
|
212
|
+
when /float/i
|
213
|
+
"float"
|
214
|
+
when /double/i
|
215
|
+
"double"
|
216
|
+
else
|
217
|
+
raise "Type name must either of int, long, string float or double"
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
234
221
|
# for fluent-plugin-td / td command to check table existence with import onlt user
|
235
222
|
# @return [String]
|
236
223
|
def self.create_empty_gz_data
|
@@ -254,7 +241,6 @@ private
|
|
254
241
|
do_get(url, params, &block)
|
255
242
|
end
|
256
243
|
end
|
257
|
-
|
258
244
|
# @param [String] url
|
259
245
|
# @param [Hash] params
|
260
246
|
# @yield [response]
|
@@ -309,7 +295,7 @@ private
|
|
309
295
|
retry_delay *= 2
|
310
296
|
redo # restart from beginning of do-while loop
|
311
297
|
end
|
312
|
-
rescue Errno::ECONNREFUSED, Errno::ECONNRESET, Timeout::Error, EOFError, OpenSSL::SSL::SSLError, SocketError, IncompleteError
|
298
|
+
rescue Errno::ECONNREFUSED, Errno::ECONNRESET, Timeout::Error, EOFError, OpenSSL::SSL::SSLError, SocketError, IncompleteError => e
|
313
299
|
if block_given?
|
314
300
|
raise e
|
315
301
|
end
|
@@ -605,15 +591,18 @@ private
|
|
605
591
|
end
|
606
592
|
|
607
593
|
def parse_error_response(res)
|
594
|
+
error = {}
|
595
|
+
|
608
596
|
begin
|
609
|
-
|
610
|
-
if
|
611
|
-
error['message']
|
597
|
+
js = JSON.load(res.body)
|
598
|
+
if js.nil?
|
599
|
+
error['message'] = res.reason
|
612
600
|
else
|
613
|
-
error
|
601
|
+
error['message'] = js['message'] || js['error']
|
602
|
+
error['stacktrace'] = js['stacktrace']
|
614
603
|
end
|
615
604
|
rescue JSON::ParserError
|
616
|
-
error
|
605
|
+
error['message'] = res.body
|
617
606
|
end
|
618
607
|
|
619
608
|
error
|
@@ -635,7 +624,6 @@ private
|
|
635
624
|
when "404"
|
636
625
|
NotFoundError
|
637
626
|
when "409"
|
638
|
-
message = "#{message}: conflicts_with job:#{error["details"]["conflicts_with"]}" if error["details"] && error["details"]["conflicts_with"]
|
639
627
|
AlreadyExistsError
|
640
628
|
when "401"
|
641
629
|
AuthError
|
@@ -647,15 +635,11 @@ private
|
|
647
635
|
end
|
648
636
|
end
|
649
637
|
|
650
|
-
|
651
|
-
|
652
|
-
exc = error_class.new(message, error['stacktrace'], error["details"]["conflicts_with"])
|
653
|
-
elsif error_class.method_defined?(:api_backtrace)
|
654
|
-
exc = error_class.new(message, error['stacktrace'])
|
638
|
+
if error_class.method_defined?(:api_backtrace)
|
639
|
+
raise error_class.new(message, error['stacktrace'])
|
655
640
|
else
|
656
|
-
|
641
|
+
raise error_class, message
|
657
642
|
end
|
658
|
-
raise exc
|
659
643
|
end
|
660
644
|
|
661
645
|
if ''.respond_to?(:encode)
|
@@ -161,7 +161,8 @@ module BulkImport
|
|
161
161
|
end
|
162
162
|
end
|
163
163
|
require File.expand_path('../compat_gzip_reader', File.dirname(__FILE__))
|
164
|
-
|
164
|
+
io = StringIO.new(Zlib::GzipReader.new(StringIO.new(body)).read)
|
165
|
+
u = MessagePack::Unpacker.new(io)
|
165
166
|
if block
|
166
167
|
begin
|
167
168
|
u.each(&block)
|
@@ -116,10 +116,10 @@ module BulkLoad
|
|
116
116
|
JSON.load(res.body)
|
117
117
|
end
|
118
118
|
|
119
|
-
# name: String,
|
120
|
-
def bulk_load_update(name,
|
119
|
+
# name: String, job: Hash -> Hash
|
120
|
+
def bulk_load_update(name, job)
|
121
121
|
path = session_path(name)
|
122
|
-
res = api { put(path,
|
122
|
+
res = api { put(path, job.to_json) }
|
123
123
|
unless res.ok?
|
124
124
|
raise_error("BulkLoadSession: #{name} update failed", res)
|
125
125
|
end
|
data/lib/td/client/api/export.rb
CHANGED
@@ -22,17 +22,5 @@ module Export
|
|
22
22
|
return js['job_id'].to_s
|
23
23
|
end
|
24
24
|
|
25
|
-
# => jobId:String
|
26
|
-
# @param [String] target_job_id
|
27
|
-
# @param [Hash] opts
|
28
|
-
# @return [String] job_id
|
29
|
-
def result_export(target_job_id, opts={})
|
30
|
-
code, body, res = post("/v3/job/result_export/#{target_job_id}", opts)
|
31
|
-
if code != "200"
|
32
|
-
raise_error("Result Export failed", res)
|
33
|
-
end
|
34
|
-
js = checked_json(body, %w[job_id])
|
35
|
-
return js['job_id'].to_s
|
36
|
-
end
|
37
25
|
end
|
38
26
|
end
|
data/lib/td/client/api/import.rb
CHANGED
@@ -21,9 +21,8 @@ module Import
|
|
21
21
|
opts = {}
|
22
22
|
if @host == DEFAULT_ENDPOINT
|
23
23
|
opts[:host] = DEFAULT_IMPORT_ENDPOINT
|
24
|
-
elsif @host ==
|
25
|
-
opts[:host] =
|
26
|
-
opts[:ssl] = false
|
24
|
+
elsif @host == NEW_DEFAULT_ENDPOINT
|
25
|
+
opts[:host] = NEW_DEFAULT_IMPORT_ENDPOINT
|
27
26
|
end
|
28
27
|
code, body, res = put(path, stream, size, opts)
|
29
28
|
if code[0] != ?2
|
data/lib/td/client/api/job.rb
CHANGED
@@ -36,10 +36,9 @@ module Job
|
|
36
36
|
priority = m['priority']
|
37
37
|
retry_limit = m['retry_limit']
|
38
38
|
duration = m['duration']
|
39
|
-
num_records = m['num_records']
|
40
39
|
result << [job_id, type, status, query, start_at, end_at, cpu_time,
|
41
40
|
result_size, result_url, priority, retry_limit, nil, database,
|
42
|
-
duration
|
41
|
+
duration]
|
43
42
|
}
|
44
43
|
return result
|
45
44
|
end
|
@@ -64,7 +63,6 @@ module Job
|
|
64
63
|
end_at = js['end_at']
|
65
64
|
cpu_time = js['cpu_time']
|
66
65
|
result_size = js['result_size'] # compressed result size in msgpack.gz format
|
67
|
-
num_records = js['num_records']
|
68
66
|
result = js['result'] # result target URL
|
69
67
|
hive_result_schema = (js['hive_result_schema'] || '')
|
70
68
|
if hive_result_schema.empty?
|
@@ -99,7 +97,7 @@ module Job
|
|
99
97
|
priority = js['priority']
|
100
98
|
retry_limit = js['retry_limit']
|
101
99
|
return [type, query, status, url, debug, start_at, end_at, cpu_time,
|
102
|
-
result_size, result, hive_result_schema, priority, retry_limit, nil, database
|
100
|
+
result_size, result, hive_result_schema, priority, retry_limit, nil, database]
|
103
101
|
end
|
104
102
|
|
105
103
|
# @param [String] job_id
|
@@ -117,13 +115,14 @@ module Job
|
|
117
115
|
# @param [String] job_id
|
118
116
|
# @return [Array]
|
119
117
|
def job_result(job_id)
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
unpacker.feed_each(chunk) do |row|
|
124
|
-
result << row
|
125
|
-
end
|
118
|
+
code, body, res = get("/v3/job/result/#{e job_id}", {'format'=>'msgpack'})
|
119
|
+
if code != "200"
|
120
|
+
raise_error("Get job result failed", res)
|
126
121
|
end
|
122
|
+
result = []
|
123
|
+
MessagePack::Unpacker.new.feed_each(body) {|row|
|
124
|
+
result << row
|
125
|
+
}
|
127
126
|
return result
|
128
127
|
end
|
129
128
|
|
@@ -134,17 +133,24 @@ module Job
|
|
134
133
|
# @param [IO] io
|
135
134
|
# @param [Proc] block
|
136
135
|
# @return [nil, String]
|
137
|
-
def job_result_format(job_id, format, io=nil)
|
136
|
+
def job_result_format(job_id, format, io=nil, &block)
|
138
137
|
if io
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
138
|
+
infl = nil
|
139
|
+
code, body, res = get("/v3/job/result/#{e job_id}", {'format'=>format}) {|res, chunk, current_total_chunk_size|
|
140
|
+
if res.code != 200
|
141
|
+
raise_error("Get job result failed", res)
|
142
|
+
end
|
143
|
+
|
144
|
+
infl ||= create_inflalte_or_null_inflate(res)
|
145
|
+
|
146
|
+
io.write infl.inflate(chunk)
|
147
|
+
block.call(current_total_chunk_size) if block_given?
|
148
|
+
}
|
143
149
|
nil
|
144
150
|
else
|
145
|
-
body =
|
146
|
-
|
147
|
-
|
151
|
+
code, body, res = get("/v3/job/result/#{e job_id}", {'format'=>format})
|
152
|
+
if code != "200"
|
153
|
+
raise_error("Get job result failed", res)
|
148
154
|
end
|
149
155
|
body
|
150
156
|
end
|
@@ -157,11 +163,22 @@ module Job
|
|
157
163
|
# @return [nil]
|
158
164
|
def job_result_each(job_id, &block)
|
159
165
|
upkr = MessagePack::Unpacker.new
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
166
|
+
infl = nil
|
167
|
+
|
168
|
+
get("/v3/job/result/#{e job_id}", {'format'=>'msgpack'}) {|res, chunk, current_total_chunk_size|
|
169
|
+
if res.code != 200
|
170
|
+
raise_error("Get job result failed", res)
|
171
|
+
end
|
172
|
+
|
173
|
+
# default to decompressing the response since format is fixed to 'msgpack'
|
174
|
+
infl ||= create_inflate(res)
|
175
|
+
|
176
|
+
inflated_fragment = infl.inflate(chunk)
|
177
|
+
upkr.feed_each(inflated_fragment, &block)
|
178
|
+
}
|
164
179
|
nil
|
180
|
+
ensure
|
181
|
+
infl.close if infl
|
165
182
|
end
|
166
183
|
|
167
184
|
# block is optional and must accept 1 argument
|
@@ -169,30 +186,50 @@ module Job
|
|
169
186
|
# @param [String] job_id
|
170
187
|
# @param [Proc] block
|
171
188
|
# @return [nil]
|
172
|
-
def job_result_each_with_compr_size(job_id)
|
189
|
+
def job_result_each_with_compr_size(job_id, &block)
|
173
190
|
upkr = MessagePack::Unpacker.new
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
191
|
+
infl = nil
|
192
|
+
|
193
|
+
get("/v3/job/result/#{e job_id}", {'format'=>'msgpack'}) {|res, chunk, current_total_chunk_size|
|
194
|
+
if res.code != 200
|
195
|
+
raise_error("Get job result failed", res)
|
196
|
+
end
|
197
|
+
|
198
|
+
# default to decompressing the response since format is fixed to 'msgpack'
|
199
|
+
infl ||= create_inflate(res)
|
200
|
+
|
201
|
+
inflated_fragment = infl.inflate(chunk)
|
202
|
+
upkr.feed_each(inflated_fragment) {|unpacked|
|
203
|
+
block.call(unpacked, current_total_chunk_size) if block_given?
|
178
204
|
}
|
179
|
-
|
205
|
+
}
|
180
206
|
nil
|
207
|
+
ensure
|
208
|
+
infl.close if infl
|
181
209
|
end
|
182
210
|
|
183
211
|
# @param [String] job_id
|
184
212
|
# @param [String] format
|
185
213
|
# @return [String]
|
186
|
-
def job_result_raw(job_id, format, io = nil)
|
187
|
-
body =
|
188
|
-
|
214
|
+
def job_result_raw(job_id, format, io = nil, &block)
|
215
|
+
body = nil
|
216
|
+
|
217
|
+
get("/v3/job/result/#{e job_id}", {'format'=>format}) {|res, chunk, current_total_chunk_size|
|
218
|
+
if res.code != 200
|
219
|
+
raise_error("Get job result failed", res)
|
220
|
+
end
|
221
|
+
|
189
222
|
if io
|
190
223
|
io.write(chunk)
|
191
|
-
|
224
|
+
block.call(current_total_chunk_size) if block_given?
|
192
225
|
else
|
193
|
-
body
|
226
|
+
if body
|
227
|
+
body += chunk
|
228
|
+
else
|
229
|
+
body = chunk
|
230
|
+
end
|
194
231
|
end
|
195
|
-
|
232
|
+
}
|
196
233
|
body
|
197
234
|
end
|
198
235
|
|
@@ -250,117 +287,6 @@ module Job
|
|
250
287
|
|
251
288
|
private
|
252
289
|
|
253
|
-
def validate_content_length_with_range(response, current_total_chunk_size)
|
254
|
-
if expected_size = response.header['Content-Range'][0]
|
255
|
-
expected_size = expected_size[/\d+$/].to_i
|
256
|
-
elsif expected_size = response.header['Content-Length'][0]
|
257
|
-
expected_size = expected_size.to_i
|
258
|
-
end
|
259
|
-
|
260
|
-
if expected_size.nil?
|
261
|
-
elsif current_total_chunk_size < expected_size
|
262
|
-
# too small
|
263
|
-
# NOTE:
|
264
|
-
# ext/openssl raises EOFError in case where underlying connection
|
265
|
-
# causes an error, but httpclient ignores it.
|
266
|
-
# https://github.com/nahi/httpclient/blob/v3.2.8/lib/httpclient/session.rb#L1003
|
267
|
-
raise EOFError, 'httpclient IncompleteError'
|
268
|
-
elsif current_total_chunk_size > expected_size
|
269
|
-
# too large
|
270
|
-
raise_error("Get job result failed", response)
|
271
|
-
end
|
272
|
-
end
|
273
|
-
|
274
|
-
def job_result_download(job_id, format='msgpack', autodecode=true)
|
275
|
-
client, header = new_client
|
276
|
-
client.send_timeout = @send_timeout
|
277
|
-
client.receive_timeout = @read_timeout
|
278
|
-
header['Accept-Encoding'] = 'deflate, gzip'
|
279
|
-
|
280
|
-
url = build_endpoint("/v3/job/result/#{e job_id}", @host)
|
281
|
-
params = {'format' => format}
|
282
|
-
|
283
|
-
unless ENV['TD_CLIENT_DEBUG'].nil?
|
284
|
-
puts "DEBUG: REST GET call:"
|
285
|
-
puts "DEBUG: header: " + header.to_s
|
286
|
-
puts "DEBUG: url: " + url.to_s
|
287
|
-
puts "DEBUG: params: " + params.to_s
|
288
|
-
end
|
289
|
-
|
290
|
-
# up to 7 retries with exponential (base 2) back-off starting at 'retry_delay'
|
291
|
-
retry_delay = @retry_delay
|
292
|
-
cumul_retry_delay = 0
|
293
|
-
current_total_chunk_size = 0
|
294
|
-
infl = nil
|
295
|
-
begin # LOOP of Network/Server errors
|
296
|
-
response = nil
|
297
|
-
client.get(url, params, header) do |res, chunk|
|
298
|
-
unless response
|
299
|
-
case res.status
|
300
|
-
when 200
|
301
|
-
if current_total_chunk_size != 0
|
302
|
-
# try to resume but the server returns 200
|
303
|
-
raise_error("Get job result failed", res)
|
304
|
-
end
|
305
|
-
when 206 # resuming
|
306
|
-
else
|
307
|
-
if res.status/100 == 5 && cumul_retry_delay < @max_cumul_retry_delay
|
308
|
-
$stderr.puts "Error #{res.status}: #{get_error(res)}. Retrying after #{retry_delay} seconds..."
|
309
|
-
sleep retry_delay
|
310
|
-
cumul_retry_delay += retry_delay
|
311
|
-
retry_delay *= 2
|
312
|
-
redo
|
313
|
-
end
|
314
|
-
raise_error("Get job result failed", res)
|
315
|
-
end
|
316
|
-
if infl.nil? && autodecode
|
317
|
-
case res.header['Content-Encoding'][0].to_s.downcase
|
318
|
-
when 'gzip'
|
319
|
-
infl = Zlib::Inflate.new(Zlib::MAX_WBITS + 16)
|
320
|
-
when 'deflate'
|
321
|
-
infl = Zlib::Inflate.new
|
322
|
-
end
|
323
|
-
end
|
324
|
-
end
|
325
|
-
response = res
|
326
|
-
current_total_chunk_size += chunk.bytesize
|
327
|
-
chunk = infl.inflate(chunk) if infl
|
328
|
-
yield chunk, current_total_chunk_size
|
329
|
-
end
|
330
|
-
|
331
|
-
# completed?
|
332
|
-
validate_content_length_with_range(response, current_total_chunk_size)
|
333
|
-
rescue Errno::ECONNREFUSED, Errno::ECONNRESET, Timeout::Error, EOFError, OpenSSL::SSL::SSLError, SocketError => e
|
334
|
-
if response # at least a chunk is downloaded
|
335
|
-
if etag = response.header['ETag'][0]
|
336
|
-
header['If-Range'] = etag
|
337
|
-
header['Range'] = "bytes=#{current_total_chunk_size}-"
|
338
|
-
end
|
339
|
-
end
|
340
|
-
|
341
|
-
$stderr.print "#{e.class}: #{e.message}. "
|
342
|
-
if cumul_retry_delay < @max_cumul_retry_delay
|
343
|
-
$stderr.puts "Retrying after #{retry_delay} seconds..."
|
344
|
-
sleep retry_delay
|
345
|
-
cumul_retry_delay += retry_delay
|
346
|
-
retry_delay *= 2
|
347
|
-
retry
|
348
|
-
end
|
349
|
-
raise
|
350
|
-
end
|
351
|
-
|
352
|
-
unless ENV['TD_CLIENT_DEBUG'].nil?
|
353
|
-
puts "DEBUG: REST GET response:"
|
354
|
-
puts "DEBUG: header: " + response.header.to_s
|
355
|
-
puts "DEBUG: status: " + response.code.to_s
|
356
|
-
puts "DEBUG: body: " + response.body.to_s
|
357
|
-
end
|
358
|
-
|
359
|
-
nil
|
360
|
-
ensure
|
361
|
-
infl.close if infl
|
362
|
-
end
|
363
|
-
|
364
290
|
class NullInflate
|
365
291
|
def inflate(chunk)
|
366
292
|
chunk
|