aspera-cli 4.19.0 → 4.20.0
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
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +20 -0
- data/CONTRIBUTING.md +16 -4
- data/README.md +344 -164
- data/bin/asession +26 -19
- data/examples/build_exec +65 -76
- data/examples/build_exec_rubyc +40 -0
- data/examples/get_proto_file.rb +7 -0
- data/lib/aspera/agent/alpha.rb +8 -8
- data/lib/aspera/agent/base.rb +2 -18
- data/lib/aspera/agent/connect.rb +14 -13
- data/lib/aspera/agent/direct.rb +23 -24
- data/lib/aspera/agent/httpgw.rb +2 -3
- data/lib/aspera/agent/node.rb +10 -10
- data/lib/aspera/agent/trsdk.rb +17 -20
- data/lib/aspera/api/alee.rb +15 -0
- data/lib/aspera/api/aoc.rb +126 -97
- data/lib/aspera/api/ats.rb +1 -1
- data/lib/aspera/api/cos_node.rb +1 -1
- data/lib/aspera/api/httpgw.rb +15 -10
- data/lib/aspera/api/node.rb +33 -12
- data/lib/aspera/ascmd.rb +56 -48
- data/lib/aspera/ascp/installation.rb +99 -42
- data/lib/aspera/ascp/management.rb +3 -2
- data/lib/aspera/ascp/products.rb +12 -0
- data/lib/aspera/assert.rb +10 -5
- data/lib/aspera/cli/formatter.rb +27 -17
- data/lib/aspera/cli/hints.rb +2 -1
- data/lib/aspera/cli/info.rb +12 -10
- data/lib/aspera/cli/main.rb +16 -13
- data/lib/aspera/cli/manager.rb +5 -0
- data/lib/aspera/cli/plugin.rb +15 -29
- data/lib/aspera/cli/plugins/alee.rb +3 -3
- data/lib/aspera/cli/plugins/aoc.rb +222 -194
- data/lib/aspera/cli/plugins/ats.rb +16 -14
- data/lib/aspera/cli/plugins/config.rb +53 -45
- data/lib/aspera/cli/plugins/console.rb +3 -3
- data/lib/aspera/cli/plugins/faspex.rb +11 -21
- data/lib/aspera/cli/plugins/faspex5.rb +44 -42
- data/lib/aspera/cli/plugins/faspio.rb +2 -2
- data/lib/aspera/cli/plugins/httpgw.rb +1 -1
- data/lib/aspera/cli/plugins/node.rb +153 -95
- data/lib/aspera/cli/plugins/orchestrator.rb +14 -13
- data/lib/aspera/cli/plugins/preview.rb +8 -9
- data/lib/aspera/cli/plugins/server.rb +5 -9
- data/lib/aspera/cli/plugins/shares.rb +2 -2
- data/lib/aspera/cli/sync_actions.rb +2 -2
- data/lib/aspera/cli/transfer_agent.rb +12 -14
- data/lib/aspera/cli/transfer_progress.rb +35 -17
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/command_line_builder.rb +3 -4
- data/lib/aspera/coverage.rb +13 -1
- data/lib/aspera/environment.rb +34 -18
- data/lib/aspera/faspex_gw.rb +2 -2
- data/lib/aspera/json_rpc.rb +1 -1
- data/lib/aspera/keychain/macos_security.rb +7 -12
- data/lib/aspera/log.rb +3 -4
- data/lib/aspera/oauth/base.rb +39 -45
- data/lib/aspera/oauth/factory.rb +11 -4
- data/lib/aspera/oauth/generic.rb +4 -8
- data/lib/aspera/oauth/jwt.rb +3 -3
- data/lib/aspera/oauth/url_json.rb +1 -2
- data/lib/aspera/oauth/web.rb +5 -2
- data/lib/aspera/persistency_action_once.rb +16 -8
- data/lib/aspera/preview/utils.rb +5 -16
- data/lib/aspera/rest.rb +100 -76
- data/lib/aspera/transfer/faux_file.rb +4 -4
- data/lib/aspera/transfer/parameters.rb +14 -16
- data/lib/aspera/transfer/spec.rb +12 -12
- data/lib/aspera/transfer/sync.rb +1 -5
- data/lib/aspera/transfer/uri.rb +1 -1
- data/lib/aspera/uri_reader.rb +1 -1
- data/lib/aspera/web_auth.rb +166 -17
- data/lib/aspera/web_server_simple.rb +4 -3
- data/lib/transfer_pb.rb +84 -0
- data/lib/transfer_services_pb.rb +82 -0
- data.tar.gz.sig +0 -0
- metadata +24 -5
- metadata.gz.sig +0 -0
data/lib/aspera/rest.rb
CHANGED
@@ -11,6 +11,8 @@ require 'net/https'
|
|
11
11
|
require 'json'
|
12
12
|
require 'base64'
|
13
13
|
require 'cgi'
|
14
|
+
require 'singleton'
|
15
|
+
require 'securerandom'
|
14
16
|
|
15
17
|
# Cancel method for HTTP
|
16
18
|
class Net::HTTP::Cancel < Net::HTTPRequest # rubocop:disable Style/ClassAndModuleChildren
|
@@ -20,22 +22,32 @@ class Net::HTTP::Cancel < Net::HTTPRequest # rubocop:disable Style/ClassAndModul
|
|
20
22
|
end
|
21
23
|
|
22
24
|
module Aspera
|
25
|
+
# Global settings
|
26
|
+
# @param user_agent [String] HTTP request header: 'User-Agent'
|
27
|
+
# @param download_partial_suffix [String] suffix for partial download
|
28
|
+
# @param session_cb [lambda] lambda called on new HTTP session. Takes the Net::HTTP as arg. Used to change parameters on creation.
|
29
|
+
# @param progress_bar [Object] progress bar object
|
30
|
+
class RestParameters
|
31
|
+
include Singleton
|
32
|
+
|
33
|
+
attr_accessor :user_agent, :download_partial_suffix, :retry_on_error, :retry_sleep, :session_cb, :progress_bar
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def initialize
|
38
|
+
@user_agent = 'RubyAsperaRest'
|
39
|
+
@download_partial_suffix = '.http_partial'
|
40
|
+
@retry_on_error = 0
|
41
|
+
@retry_sleep = nil
|
42
|
+
@session_cb = nil
|
43
|
+
@progress_bar = nil
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
23
47
|
# a simple class to make HTTP calls, equivalent to rest-client
|
24
48
|
# rest call errors are raised as exception RestCallError
|
25
49
|
# and error are analyzed in RestErrorAnalyzer
|
26
50
|
class Rest
|
27
|
-
# Global settings also valid for any subclass
|
28
|
-
# @param user_agent [String] HTTP request header: 'User-Agent'
|
29
|
-
# @param download_partial_suffix [String] suffix for partial download
|
30
|
-
# @param session_cb [lambda] lambda called on new HTTP session. Takes the Net::HTTP as arg. Used to change parameters on creation.
|
31
|
-
# @param progress_bar [Object] progress bar object
|
32
|
-
@@global = { # rubocop:disable Style/ClassVars
|
33
|
-
user_agent: 'RubyAsperaRest',
|
34
|
-
download_partial_suffix: '.http_partial',
|
35
|
-
session_cb: nil,
|
36
|
-
progress_bar: nil
|
37
|
-
}
|
38
|
-
|
39
51
|
# flag for array parameters prefixed with []
|
40
52
|
ARRAY_PARAMS = '[]'
|
41
53
|
|
@@ -61,37 +73,45 @@ module Aspera
|
|
61
73
|
return values.first.eql?(ARRAY_PARAMS)
|
62
74
|
end
|
63
75
|
|
64
|
-
# Build URI from URL and parameters and check it is http or https
|
65
|
-
|
76
|
+
# Build URI from URL and parameters and check it is http or https
|
77
|
+
# encode array [] parameters
|
78
|
+
# @param query [Hash,Array]
|
79
|
+
def build_uri(url, query=nil)
|
66
80
|
uri = URI.parse(url)
|
67
81
|
Aspera.assert(%w[http https].include?(uri.scheme)){"REST endpoint shall be http/s not #{uri.scheme}"}
|
68
|
-
return uri if
|
69
|
-
Log.log.debug{Log.dump('query',
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
82
|
+
return uri if query.nil? || query.respond_to?(:empty?) && query.empty?
|
83
|
+
Log.log.debug{Log.dump('query', query)}
|
84
|
+
query_array = []
|
85
|
+
case query
|
86
|
+
when Hash
|
87
|
+
query.each do |k, v|
|
88
|
+
case v
|
89
|
+
when Array
|
90
|
+
# support array for query parameter, there is no standard. Either p[]=1&p[]=2, or p=1&p=2
|
91
|
+
suffix = array_params?(v) ? v.shift : ''
|
92
|
+
v.each do |e|
|
93
|
+
query_array.push(["#{k}#{suffix}", e])
|
94
|
+
end
|
95
|
+
else
|
96
|
+
query_array.push([k, v])
|
80
97
|
end
|
81
|
-
else
|
82
|
-
query.push([k, v])
|
83
98
|
end
|
99
|
+
when Array
|
100
|
+
Aspera.assert(query.all?{|i| i.is_a?(Array) && i.length.eql?(2)}) {'Query must be array of arrays or 2 elements'}
|
101
|
+
query_array = query
|
102
|
+
else
|
103
|
+
raise "Query must be Hash or Array, not #{query.class}"
|
84
104
|
end
|
85
105
|
# [] is allowed in url parameters
|
86
|
-
uri.query = URI.encode_www_form(
|
106
|
+
uri.query = URI.encode_www_form(query_array).gsub('%5B%5D=', '[]=')
|
87
107
|
return uri
|
88
108
|
end
|
89
109
|
|
90
|
-
#
|
110
|
+
# Decode query string as Hash
|
91
111
|
# Does not support arrays in query string, no standard, e.g. PHP's way is p[]=1&p[]=2
|
92
|
-
# @param query [String] query string
|
112
|
+
# @param query [String] query string as in URI.query
|
93
113
|
# @return [Hash] decoded query
|
94
|
-
def
|
114
|
+
def query_to_h(query)
|
95
115
|
URI.decode_www_form(query).each_with_object({}) do |pair, h|
|
96
116
|
key = pair.first
|
97
117
|
raise "Array not supported in query string: #{key}" if key.include?('[]') || h.key?(key)
|
@@ -108,7 +128,7 @@ module Aspera
|
|
108
128
|
http_session = Net::HTTP.new(uri.host, uri.port)
|
109
129
|
http_session.use_ssl = uri.scheme.eql?('https')
|
110
130
|
# set http options in callback, such as timeout and cert. verification
|
111
|
-
|
131
|
+
RestParameters.instance.session_cb&.call(http_session)
|
112
132
|
# manually start session for keep alive (if supported by server, else, session is closed every time)
|
113
133
|
http_session.start
|
114
134
|
return http_session
|
@@ -142,19 +162,6 @@ module Aspera
|
|
142
162
|
return result
|
143
163
|
end
|
144
164
|
|
145
|
-
# set global parameters
|
146
|
-
def set_parameters(**options)
|
147
|
-
options.each do |key, value|
|
148
|
-
Aspera.assert(@@global.key?(key)){"Unknown Rest option #{key}"}
|
149
|
-
@@global[key] = value
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
# @return [String] HTTP agent name
|
154
|
-
def user_agent
|
155
|
-
return @@global[:user_agent]
|
156
|
-
end
|
157
|
-
|
158
165
|
def parse_header(header)
|
159
166
|
type, *params = header.split(/;\s*/)
|
160
167
|
parameters = params.map do |param|
|
@@ -179,16 +186,17 @@ module Aspera
|
|
179
186
|
|
180
187
|
public
|
181
188
|
|
182
|
-
attr_reader :auth_params
|
183
189
|
attr_reader :base_url
|
190
|
+
attr_reader :auth_params
|
184
191
|
|
192
|
+
# @return creation parameters
|
185
193
|
def params
|
186
194
|
return {
|
187
|
-
base_url: @base_url,
|
188
|
-
auth: @auth_params,
|
189
|
-
not_auth_codes: @not_auth_codes,
|
190
|
-
redirect_max: @redirect_max,
|
191
|
-
headers: @headers
|
195
|
+
base_url: @base_url, # String
|
196
|
+
auth: @auth_params, # Hash
|
197
|
+
not_auth_codes: @not_auth_codes, # Array
|
198
|
+
redirect_max: @redirect_max, # Integer
|
199
|
+
headers: @headers # Hash
|
192
200
|
}
|
193
201
|
end
|
194
202
|
|
@@ -226,9 +234,10 @@ module Aspera
|
|
226
234
|
# OAuth object (created on demand)
|
227
235
|
@oauth = nil
|
228
236
|
@redirect_max = redirect_max
|
237
|
+
Aspera.assert_type(@redirect_max, Integer)
|
229
238
|
@headers = headers.nil? ? {} : headers
|
230
239
|
Aspera.assert_type(@headers, Hash)
|
231
|
-
@headers['User-Agent'] ||=
|
240
|
+
@headers['User-Agent'] ||= RestParameters.instance.user_agent
|
232
241
|
end
|
233
242
|
|
234
243
|
# @return the OAuth object (create, or cached if already created)
|
@@ -263,6 +272,8 @@ module Aspera
|
|
263
272
|
)
|
264
273
|
subpath = subpath.to_s if subpath.is_a?(Symbol)
|
265
274
|
subpath = '' if subpath.nil?
|
275
|
+
Log.log.debug{"#{operation} [#{subpath}]".red.bold.bg_green}
|
276
|
+
Log.log.debug{Log.dump(:body, body)}
|
266
277
|
Aspera.assert_type(subpath, String)
|
267
278
|
if headers.nil?
|
268
279
|
headers = @headers.clone
|
@@ -272,7 +283,6 @@ module Aspera
|
|
272
283
|
headers.merge!(h)
|
273
284
|
end
|
274
285
|
Aspera.assert_type(headers, Hash)
|
275
|
-
Log.log.debug{"#{operation} [#{subpath}]".red.bold.bg_green}
|
276
286
|
case @auth_params[:type]
|
277
287
|
when :none
|
278
288
|
# no auth
|
@@ -322,11 +332,13 @@ module Aspera
|
|
322
332
|
end
|
323
333
|
# :type = :basic
|
324
334
|
req.basic_auth(@auth_params[:username], @auth_params[:password]) if @auth_params[:type].eql?(:basic)
|
325
|
-
Log.log.
|
335
|
+
Log.log.trace1{Log.dump(:req_body, req.body)}
|
326
336
|
# we try the call, and will retry only if oauth, as we can, first with refresh, and then re-auth if refresh is bad
|
327
337
|
oauth_tries ||= 2
|
338
|
+
timeout_tries ||= 5
|
339
|
+
general_tries ||= 1 + RestParameters.instance.retry_on_error
|
328
340
|
# initialize with number of initial retries allowed, nil gives zero
|
329
|
-
tries_remain_redirect = @redirect_max
|
341
|
+
tries_remain_redirect = @redirect_max if tries_remain_redirect.nil?
|
330
342
|
Log.log.debug("send request (retries=#{tries_remain_redirect})")
|
331
343
|
result_mime = nil
|
332
344
|
file_saved = false
|
@@ -350,38 +362,44 @@ module Aspera
|
|
350
362
|
end
|
351
363
|
end
|
352
364
|
# download with temp filename
|
353
|
-
target_file_tmp = "#{target_file}#{
|
365
|
+
target_file_tmp = "#{target_file}#{RestParameters.instance.download_partial_suffix}"
|
354
366
|
Log.log.debug{"saving to: #{target_file}"}
|
355
367
|
written_size = 0
|
356
|
-
|
357
|
-
|
368
|
+
session_id = SecureRandom.uuid.freeze
|
369
|
+
RestParameters.instance.progress_bar&.event(:session_start, session_id: session_id)
|
370
|
+
RestParameters.instance.progress_bar&.event(:session_size, session_id: session_id, info: total_size)
|
358
371
|
File.open(target_file_tmp, 'wb') do |file|
|
359
372
|
result[:http].read_body do |fragment|
|
360
373
|
file.write(fragment)
|
361
374
|
written_size += fragment.length
|
362
|
-
|
375
|
+
RestParameters.instance.progress_bar&.event(:transfer, session_id: session_id, info: written_size)
|
363
376
|
end
|
364
377
|
end
|
365
|
-
|
378
|
+
RestParameters.instance.progress_bar&.event(:end, session_id: session_id)
|
366
379
|
# rename at the end
|
367
380
|
File.rename(target_file_tmp, target_file)
|
368
381
|
file_saved = true
|
369
382
|
end
|
370
383
|
end
|
384
|
+
Log.log.debug{"result: code=#{result[:http].code} mime=#{result_mime}"}
|
371
385
|
# sometimes there is a UTF8 char (e.g. (c) ), TODO : related to mime type encoding ?
|
372
386
|
# result[:http].body.force_encoding('UTF-8') if result[:http].body.is_a?(String)
|
373
387
|
# Log.log.debug{"result: body=#{result[:http].body}"}
|
374
|
-
|
388
|
+
case result_mime
|
375
389
|
when *JSON_DECODE
|
376
|
-
JSON.parse(result[:http].body) rescue result[:http].body
|
390
|
+
result[:data] = JSON.parse(result[:http].body) rescue result[:http].body
|
391
|
+
Log.log.debug{Log.dump('result_data', result[:data])}
|
377
392
|
else # when 'text/plain'
|
378
|
-
result[:http].body
|
393
|
+
result[:data] = result[:http].body
|
379
394
|
end
|
380
|
-
Log.log.debug{"result: code=#{result[:http].code} mime=#{result_mime}"}
|
381
|
-
Log.log.debug{Log.dump('data', result[:data])}
|
382
395
|
RestErrorAnalyzer.instance.raise_on_error(req, result)
|
383
|
-
File.write(save_to_file, result[:http].body) unless file_saved || save_to_file.nil?
|
396
|
+
File.write(save_to_file, result[:http].body, binmode: true) unless file_saved || save_to_file.nil?
|
384
397
|
rescue RestCallError => e
|
398
|
+
do_retry = false
|
399
|
+
# AoC have some timeout , like Connect to platform.bss.asperasoft.com:443 ...
|
400
|
+
do_retry = true if e.response.body.include?('failed: connect timed out') && (timeout_tries -= 1).positive?
|
401
|
+
# possibility to retry anything if it fails
|
402
|
+
do_retry = true if (general_tries -= 1).positive?
|
385
403
|
# not authorized: oauth token expired
|
386
404
|
if @not_auth_codes.include?(result[:http].code.to_s) && @auth_params[:type].eql?(:oauth2)
|
387
405
|
begin
|
@@ -394,7 +412,11 @@ module Aspera
|
|
394
412
|
req['Authorization'] = oauth.token(refresh: true)
|
395
413
|
end
|
396
414
|
Log.log.debug{"using new token=#{headers['Authorization']}"}
|
397
|
-
|
415
|
+
do_retry = true if (oauth_tries -= 1).positive?
|
416
|
+
end
|
417
|
+
if do_retry
|
418
|
+
sleep(RestParameters.instance.retry_sleep) unless RestParameters.instance.retry_sleep.nil?
|
419
|
+
retry
|
398
420
|
end
|
399
421
|
# redirect ? (any code beginning with 3)
|
400
422
|
if e.response.is_a?(Net::HTTPRedirection) && tries_remain_redirect.positive?
|
@@ -420,27 +442,28 @@ module Aspera
|
|
420
442
|
end
|
421
443
|
|
422
444
|
#
|
423
|
-
# CRUD methods here
|
445
|
+
# CRUD simplified methods here
|
446
|
+
# If specific elements are needed, then use the full `call` method
|
424
447
|
#
|
425
448
|
|
426
449
|
def create(subpath, params)
|
427
|
-
return call(operation: 'POST', subpath: subpath, headers: {'Accept' => 'application/json'}, body: params, body_type: :json)
|
450
|
+
return call(operation: 'POST', subpath: subpath, headers: {'Accept' => 'application/json'}, body: params, body_type: :json)[:data]
|
428
451
|
end
|
429
452
|
|
430
453
|
def read(subpath, query=nil)
|
431
|
-
return call(operation: 'GET', subpath: subpath, headers: {'Accept' => 'application/json'}, query: query)
|
454
|
+
return call(operation: 'GET', subpath: subpath, headers: {'Accept' => 'application/json'}, query: query)[:data]
|
432
455
|
end
|
433
456
|
|
434
457
|
def update(subpath, params)
|
435
|
-
return call(operation: 'PUT', subpath: subpath, headers: {'Accept' => 'application/json'}, body: params, body_type: :json)
|
458
|
+
return call(operation: 'PUT', subpath: subpath, headers: {'Accept' => 'application/json'}, body: params, body_type: :json)[:data]
|
436
459
|
end
|
437
460
|
|
438
461
|
def delete(subpath, params=nil)
|
439
|
-
return call(operation: 'DELETE', subpath: subpath, headers: {'Accept' => 'application/json'}, query: params)
|
462
|
+
return call(operation: 'DELETE', subpath: subpath, headers: {'Accept' => 'application/json'}, query: params)[:data]
|
440
463
|
end
|
441
464
|
|
442
465
|
def cancel(subpath)
|
443
|
-
return call(operation: 'CANCEL', subpath: subpath, headers: {'Accept' => 'application/json'})
|
466
|
+
return call(operation: 'CANCEL', subpath: subpath, headers: {'Accept' => 'application/json'})[:data]
|
444
467
|
end
|
445
468
|
|
446
469
|
# Query entity by general search (read with parameter `q`)
|
@@ -449,9 +472,10 @@ module Aspera
|
|
449
472
|
# @param search_name name of searched entity
|
450
473
|
# @param query additional search query parameters
|
451
474
|
# @returns [Hash] A single entity matching the search, or an exception if not found or multiple found
|
452
|
-
def lookup_by_name(subpath, search_name, query
|
475
|
+
def lookup_by_name(subpath, search_name, query: nil)
|
476
|
+
query = {} if query.nil?
|
453
477
|
# returns entities matching the query (it matches against several fields in case insensitive way)
|
454
|
-
matching_items = read(subpath, query.merge({'q' => search_name}))
|
478
|
+
matching_items = read(subpath, query.merge({'q' => search_name}))
|
455
479
|
# API style: {totalcount:, ...} cspell: disable-line
|
456
480
|
matching_items = matching_items[subpath] if matching_items.is_a?(Hash)
|
457
481
|
Aspera.assert_type(matching_items, Array)
|
@@ -13,16 +13,16 @@ module Aspera
|
|
13
13
|
# @return nil if not a faux: scheme, else a FauxFile instance
|
14
14
|
def create(name)
|
15
15
|
return nil unless name.start_with?(PREFIX)
|
16
|
-
|
17
|
-
raise 'Format: #{PREFIX}<file path>?<size>' unless
|
18
|
-
raise "Format: <integer>[#{SUFFIX.join(',')}]" unless (m =
|
16
|
+
name_params = name[PREFIX.length..-1].split('?', 2)
|
17
|
+
raise 'Format: #{PREFIX}<file path>?<size>' unless name_params.length.eql?(2)
|
18
|
+
raise "Format: <integer>[#{SUFFIX.join(',')}]" unless (m = name_params[1].downcase.match(/^(\d+)([#{SUFFIX.join('')}])$/))
|
19
19
|
size = m[1].to_i
|
20
20
|
suffix = m[2]
|
21
21
|
SUFFIX.each do |s|
|
22
22
|
size *= 1024
|
23
23
|
break if s.eql?(suffix)
|
24
24
|
end
|
25
|
-
return FauxFile.new(
|
25
|
+
return FauxFile.new(name_params[0], size)
|
26
26
|
end
|
27
27
|
end
|
28
28
|
attr_reader :path, :size
|
@@ -29,10 +29,21 @@ module Aspera
|
|
29
29
|
private_constant :SUPPORTED_AGENTS, :FILE_LIST_OPTIONS
|
30
30
|
|
31
31
|
class << self
|
32
|
+
# temp file list files are created here
|
33
|
+
def file_list_folder=(value)
|
34
|
+
@file_list_folder = value
|
35
|
+
return if @file_list_folder.nil?
|
36
|
+
|
37
|
+
FileUtils.mkdir_p(@file_list_folder)
|
38
|
+
TempFileManager.instance.cleanup_expired(@file_list_folder)
|
39
|
+
end
|
40
|
+
|
32
41
|
# Temp folder for file lists, must contain only file lists
|
33
42
|
# because of garbage collection takes any file there
|
34
|
-
# this could be refined, as
|
35
|
-
|
43
|
+
# this could be refined, as, for example, on macos, temp folder is already user specific
|
44
|
+
def file_list_folder
|
45
|
+
@file_list_folder ||= TempFileManager.instance.new_file_path_global('asession_filelists')
|
46
|
+
end
|
36
47
|
|
37
48
|
# @param formatter [Cli::Formatter] formatter to use
|
38
49
|
# @return a table suitable to display in manual
|
@@ -67,9 +78,7 @@ module Aspera
|
|
67
78
|
else
|
68
79
|
param[:d].eql?(tick_yes) ? '' : 'n/a'
|
69
80
|
end
|
70
|
-
if options.key?(:enum)
|
71
|
-
param[:description] += "\nAllowed values: #{options[:enum].join(', ')}"
|
72
|
-
end
|
81
|
+
param[:description] += "\nAllowed values: #{options[:enum].join(', ')}" if options.key?(:enum)
|
73
82
|
# replace "solidus" HTML entity with its text value
|
74
83
|
param[:description] = param[:description].gsub('/', '\\')
|
75
84
|
result.push(param)
|
@@ -91,17 +100,6 @@ module Aspera
|
|
91
100
|
def ascp_args_file_list?(ascp_args)
|
92
101
|
ascp_args&.any?{|i|FILE_LIST_OPTIONS.include?(i)}
|
93
102
|
end
|
94
|
-
|
95
|
-
# temp file list files are created here
|
96
|
-
def file_list_folder=(value)
|
97
|
-
@file_list_folder = value
|
98
|
-
return if @file_list_folder.nil?
|
99
|
-
FileUtils.mkdir_p(@file_list_folder)
|
100
|
-
TempFileManager.instance.cleanup_expired(@file_list_folder)
|
101
|
-
end
|
102
|
-
|
103
|
-
# static methods
|
104
|
-
attr_reader :file_list_folder
|
105
103
|
end
|
106
104
|
|
107
105
|
# @param options [Hash] key: :wss: bool, :ascp_args: array of strings
|
data/lib/aspera/transfer/spec.rb
CHANGED
@@ -10,34 +10,30 @@ module Aspera
|
|
10
10
|
class Spec
|
11
11
|
# default transfer username for access key based transfers
|
12
12
|
ACCESS_KEY_TRANSFER_USER = 'xfer'
|
13
|
+
# default ports for SSH and UDP
|
13
14
|
SSH_PORT = 33_001
|
14
15
|
UDP_PORT = 33_001
|
16
|
+
# base transfer spec for access keys
|
15
17
|
AK_TSPEC_BASE = {
|
16
18
|
'remote_user' => ACCESS_KEY_TRANSFER_USER,
|
17
19
|
'ssh_port' => SSH_PORT,
|
18
20
|
'fasp_port' => UDP_PORT
|
19
21
|
}.freeze
|
20
|
-
# fields for
|
21
|
-
|
22
|
+
# fields for WSS
|
23
|
+
WSS_FIELDS = %w[wss_enabled wss_port].freeze
|
24
|
+
# all fields for transport
|
25
|
+
TRANSPORT_FIELDS = %w[remote_host remote_user ssh_port fasp_port].concat(WSS_FIELDS).freeze
|
22
26
|
# reserved tag for Aspera
|
23
27
|
TAG_RESERVED = 'aspera'
|
24
28
|
class << self
|
25
29
|
# translate upload/download to send/receive
|
26
30
|
def transfer_type_to_direction(transfer_type)
|
27
|
-
|
28
|
-
when :upload then DIRECTION_SEND
|
29
|
-
when :download then DIRECTION_RECEIVE
|
30
|
-
else Aspera.error_unexpected_value(transfer_type.to_sym)
|
31
|
-
end
|
31
|
+
XFER_TYPE_TO_DIR.fetch(transfer_type)
|
32
32
|
end
|
33
33
|
|
34
34
|
# translate send/receive to upload/download
|
35
35
|
def direction_to_transfer_type(direction)
|
36
|
-
|
37
|
-
when DIRECTION_SEND then :upload
|
38
|
-
when DIRECTION_RECEIVE then :download
|
39
|
-
else Aspera.error_unexpected_value(direction)
|
40
|
-
end
|
36
|
+
XFER_DIR_TO_TYPE.fetch(direction)
|
41
37
|
end
|
42
38
|
end
|
43
39
|
DESCRIPTION = CommandLineBuilder.normalize_description(YAML.load_file("#{__FILE__[0..-3]}yaml"))
|
@@ -49,6 +45,10 @@ module Aspera
|
|
49
45
|
const_set("#{name.to_s.upcase}_#{enum.upcase.gsub(/[^A-Z0-9]/, '_')}", enum.freeze)
|
50
46
|
end
|
51
47
|
end
|
48
|
+
# DIRECTION_* are read from yaml
|
49
|
+
XFER_TYPE_TO_DIR = {upload: DIRECTION_SEND, download: DIRECTION_RECEIVE}.freeze
|
50
|
+
XFER_DIR_TO_TYPE = XFER_TYPE_TO_DIR.invert.freeze
|
51
|
+
private_constant :XFER_TYPE_TO_DIR, :XFER_DIR_TO_TYPE
|
52
52
|
end
|
53
53
|
end
|
54
54
|
end
|
data/lib/aspera/transfer/sync.rb
CHANGED
@@ -266,11 +266,7 @@ module Aspera
|
|
266
266
|
else
|
267
267
|
raise 'At least one of `local` or `sessions` must be present in async parameters'
|
268
268
|
end
|
269
|
-
Environment.
|
270
|
-
stdout, stderr, status = Open3.capture3(*[ASYNC_ADMIN_EXECUTABLE].concat(arguments))
|
271
|
-
Log.log.debug{"status=#{status}, stderr=#{stderr}"}
|
272
|
-
Log.log.trace1{"stdout=#{stdout}"}
|
273
|
-
raise "Sync failed: #{status.exitstatus} : #{stderr}" unless status.success?
|
269
|
+
stdout = Environment.secure_capture(exec: ASYNC_ADMIN_EXECUTABLE, args: arguments)
|
274
270
|
return parse_status(stdout)
|
275
271
|
end
|
276
272
|
end
|
data/lib/aspera/transfer/uri.rb
CHANGED
@@ -26,7 +26,7 @@ module Aspera
|
|
26
26
|
# faspex 4 does not encode trailing base64 padding, fix that to be able to decode properly
|
27
27
|
fixed_query = @fasp_uri.query.gsub(/(=+)$/){|trail_equals|'%3D' * trail_equals.length}
|
28
28
|
|
29
|
-
Rest.
|
29
|
+
Rest.query_to_h(fixed_query).each do |name, value|
|
30
30
|
case name
|
31
31
|
when 'cookie' then result_ts['cookie'] = value
|
32
32
|
when 'token' then result_ts['token'] = value
|
data/lib/aspera/uri_reader.rb
CHANGED
@@ -12,7 +12,7 @@ module Aspera
|
|
12
12
|
uri = URI.parse(uri_to_read)
|
13
13
|
case uri.scheme
|
14
14
|
when 'http', 'https'
|
15
|
-
return Rest.new(base_url: uri_to_read, redirect_max: 5).call(operation: 'GET',
|
15
|
+
return Rest.new(base_url: uri_to_read, redirect_max: 5).call(operation: 'GET', headers: {'Accept' => '*/*'})[:data]
|
16
16
|
when 'file', NilClass
|
17
17
|
local_file_path = uri.path
|
18
18
|
raise 'URL shall have a path, check syntax' if local_file_path.nil?
|