aspera-cli 4.11.0 → 4.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/BUGS.md +0 -1
- data/CHANGELOG.md +71 -52
- data/CONTRIBUTING.md +31 -6
- data/README.md +404 -259
- data/bin/asession +2 -2
- data/docs/test_env.conf +1 -0
- data/examples/aoc.rb +2 -2
- data/examples/dascli +11 -11
- data/examples/faspex4.rb +7 -7
- data/examples/node.rb +1 -1
- data/examples/proxy.pac +2 -2
- data/examples/server.rb +3 -3
- data/lib/aspera/aoc.rb +105 -40
- data/lib/aspera/cli/extended_value.rb +4 -4
- data/lib/aspera/cli/{formater.rb → formatter.rb} +7 -7
- data/lib/aspera/cli/listener/progress.rb +1 -1
- data/lib/aspera/cli/listener/progress_multi.rb +2 -2
- data/lib/aspera/cli/main.rb +18 -18
- data/lib/aspera/cli/manager.rb +5 -5
- data/lib/aspera/cli/plugin.rb +23 -20
- data/lib/aspera/cli/plugins/aoc.rb +75 -112
- data/lib/aspera/cli/plugins/ats.rb +6 -6
- data/lib/aspera/cli/plugins/config.rb +84 -83
- data/lib/aspera/cli/plugins/cos.rb +1 -1
- data/lib/aspera/cli/plugins/faspex.rb +38 -38
- data/lib/aspera/cli/plugins/faspex5.rb +187 -43
- data/lib/aspera/cli/plugins/node.rb +30 -37
- data/lib/aspera/cli/plugins/orchestrator.rb +7 -4
- data/lib/aspera/cli/plugins/preview.rb +10 -9
- data/lib/aspera/cli/plugins/server.rb +1 -1
- data/lib/aspera/cli/plugins/shares.rb +67 -43
- data/lib/aspera/cli/transfer_agent.rb +16 -16
- data/lib/aspera/cli/version.rb +2 -1
- data/lib/aspera/command_line_builder.rb +70 -66
- data/lib/aspera/cos_node.rb +9 -9
- data/lib/aspera/fasp/agent_base.rb +3 -1
- data/lib/aspera/fasp/agent_connect.rb +23 -23
- data/lib/aspera/fasp/agent_direct.rb +13 -14
- data/lib/aspera/fasp/agent_httpgw.rb +20 -19
- data/lib/aspera/fasp/agent_node.rb +13 -15
- data/lib/aspera/fasp/agent_trsdk.rb +1 -1
- data/lib/aspera/fasp/installation.rb +5 -5
- data/lib/aspera/fasp/listener.rb +1 -1
- data/lib/aspera/fasp/parameters.rb +49 -41
- data/lib/aspera/fasp/parameters.yaml +311 -212
- data/lib/aspera/fasp/resume_policy.rb +2 -2
- data/lib/aspera/fasp/transfer_spec.rb +0 -13
- data/lib/aspera/faspex_gw.rb +80 -161
- data/lib/aspera/faspex_postproc.rb +77 -0
- data/lib/aspera/log.rb +7 -7
- data/lib/aspera/nagios.rb +6 -6
- data/lib/aspera/node.rb +24 -19
- data/lib/aspera/oauth.rb +50 -47
- data/lib/aspera/proxy_auto_config.js +22 -22
- data/lib/aspera/proxy_auto_config.rb +3 -3
- data/lib/aspera/rest.rb +12 -10
- data/lib/aspera/rest_error_analyzer.rb +5 -5
- data/lib/aspera/secret_hider.rb +4 -3
- data/lib/aspera/ssh.rb +4 -4
- data/lib/aspera/sync.rb +37 -36
- data/lib/aspera/web_auth.rb +7 -59
- data/lib/aspera/web_server_simple.rb +76 -0
- data.tar.gz.sig +0 -0
- metadata +6 -4
- metadata.gz.sig +0 -0
@@ -9,28 +9,28 @@ require 'tty-spinner'
|
|
9
9
|
module Aspera
|
10
10
|
module Fasp
|
11
11
|
class AgentConnect < Aspera::Fasp::AgentBase
|
12
|
-
|
12
|
+
CONNECT_START_URIS = ['fasp://initialize', 'fasp://initialize', 'aspera-drive://initialize', 'https://test-connect.ibmaspera.com/']
|
13
13
|
SLEEP_SEC_BETWEEN_RETRY = 3
|
14
|
-
private_constant :
|
14
|
+
private_constant :CONNECT_START_URIS, :SLEEP_SEC_BETWEEN_RETRY
|
15
15
|
def initialize(_options)
|
16
16
|
super()
|
17
17
|
@connect_settings = {
|
18
18
|
'app_id' => SecureRandom.uuid
|
19
19
|
}
|
20
20
|
raise 'Using connect requires a graphical environment' if !OpenApplication.default_gui_mode.eql?(:graphical)
|
21
|
-
|
21
|
+
method_index = 0
|
22
22
|
begin
|
23
23
|
connect_url = Installation.instance.connect_uri
|
24
24
|
Log.log.debug{"found: #{connect_url}"}
|
25
25
|
@connect_api = Rest.new({base_url: "#{connect_url}/v5/connect", headers: {'Origin' => Rest.user_agent}}) # could use v6 also now
|
26
|
-
|
27
|
-
Log.log.info('Connect was reached') if
|
28
|
-
Log.dump(:connect_version,
|
26
|
+
connect_info = @connect_api.read('info/version')[:data]
|
27
|
+
Log.log.info('Connect was reached') if method_index > 0
|
28
|
+
Log.dump(:connect_version, connect_info)
|
29
29
|
rescue StandardError => e # Errno::ECONNREFUSED
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
30
|
+
start_url = CONNECT_START_URIS[method_index]
|
31
|
+
method_index += 1
|
32
|
+
raise StandardError, "Unable to start connect #{method_index} times" if start_url.nil?
|
33
|
+
Log.log.warn{"Aspera Connect is not started (#{e}). Trying to start it ##{method_index}..."}
|
34
34
|
if !OpenApplication.uri_graphical(start_url)
|
35
35
|
OpenApplication.uri_graphical('https://downloads.asperasoft.com/connect2/')
|
36
36
|
raise StandardError, 'Connect is not installed'
|
@@ -40,17 +40,17 @@ module Aspera
|
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
-
def start_transfer(transfer_spec)
|
43
|
+
def start_transfer(transfer_spec, token_regenerator: nil)
|
44
44
|
if transfer_spec['direction'] == 'send'
|
45
45
|
Log.log.warn{"Connect requires upload selection using GUI, ignoring #{transfer_spec['paths']}".red}
|
46
46
|
transfer_spec.delete('paths')
|
47
|
-
|
47
|
+
selection = @connect_api.create('windows/select-open-file-dialog/', {
|
48
48
|
'aspera_connect_settings' => @connect_settings,
|
49
49
|
'title' => 'Select Files',
|
50
50
|
'suggestedName' => '',
|
51
51
|
'allowMultipleSelection' => true,
|
52
52
|
'allowedFileTypes' => ''})[:data]
|
53
|
-
transfer_spec['paths'] =
|
53
|
+
transfer_spec['paths'] = selection['dataTransfer']['files'].map { |i| {'source' => i['name']}}
|
54
54
|
end
|
55
55
|
@request_id = SecureRandom.uuid
|
56
56
|
# if there is a token, we ask connect client to use well known ssh private keys
|
@@ -77,13 +77,13 @@ module Aspera
|
|
77
77
|
loop do
|
78
78
|
tr_info = @connect_api.create("transfers/info/#{@xfer_id}", connect_activity_args)[:data]
|
79
79
|
if tr_info['transfer_info'].is_a?(Hash)
|
80
|
-
|
81
|
-
if
|
80
|
+
transfer = tr_info['transfer_info']
|
81
|
+
if transfer.nil?
|
82
82
|
Log.log.warn('no session in Connect')
|
83
83
|
break
|
84
84
|
end
|
85
85
|
# TODO: get session id
|
86
|
-
case
|
86
|
+
case transfer['status']
|
87
87
|
when 'completed'
|
88
88
|
notify_end(@connect_settings['app_id'])
|
89
89
|
break
|
@@ -92,22 +92,22 @@ module Aspera
|
|
92
92
|
spinner = TTY::Spinner.new('[:spinner] :title', format: :classic)
|
93
93
|
spinner.start
|
94
94
|
end
|
95
|
-
spinner.update(title:
|
95
|
+
spinner.update(title: transfer['status'])
|
96
96
|
spinner.spin
|
97
97
|
when 'running'
|
98
|
-
# puts "running: sessions:#{
|
99
|
-
if !started && (
|
98
|
+
# puts "running: sessions:#{transfer['sessions'].length}, #{transfer['sessions'].map{|i| i['bytes_transferred']}.join(',')}"
|
99
|
+
if !started && (transfer['bytes_expected'] != 0)
|
100
100
|
spinner&.success
|
101
|
-
notify_begin(@connect_settings['app_id'],
|
101
|
+
notify_begin(@connect_settings['app_id'], transfer['bytes_expected'])
|
102
102
|
started = true
|
103
103
|
else
|
104
|
-
notify_progress(@connect_settings['app_id'],
|
104
|
+
notify_progress(@connect_settings['app_id'], transfer['bytes_written'])
|
105
105
|
end
|
106
106
|
when 'failed'
|
107
107
|
spinner&.error
|
108
|
-
raise Fasp::Error,
|
108
|
+
raise Fasp::Error, transfer['error_desc']
|
109
109
|
else
|
110
|
-
raise Fasp::Error, "unknown status: #{
|
110
|
+
raise Fasp::Error, "unknown status: #{transfer['status']}: #{transfer['error_desc']}"
|
111
111
|
end
|
112
112
|
end
|
113
113
|
sleep(1)
|
@@ -24,6 +24,7 @@ module Aspera
|
|
24
24
|
wss: true, # true: if both SSH and wss in ts: prefer wss
|
25
25
|
multi_incr_udp: true,
|
26
26
|
resume: {},
|
27
|
+
ascp_args: [],
|
27
28
|
quiet: true # by default no interactive progress bar
|
28
29
|
}.freeze
|
29
30
|
private_constant :DEFAULT_OPTIONS
|
@@ -31,7 +32,7 @@ module Aspera
|
|
31
32
|
# start ascp transfer (non blocking), single or multi-session
|
32
33
|
# job information added to @jobs
|
33
34
|
# @param transfer_spec [Hash] aspera transfer specification
|
34
|
-
def start_transfer(transfer_spec)
|
35
|
+
def start_transfer(transfer_spec, token_regenerator: nil)
|
35
36
|
the_job_id = SecureRandom.uuid
|
36
37
|
# clone transfer spec because we modify it (first level keys)
|
37
38
|
transfer_spec = transfer_spec.clone
|
@@ -52,7 +53,7 @@ module Aspera
|
|
52
53
|
if transfer_spec.key?('token') &&
|
53
54
|
!transfer_spec.key?('remote_password') &&
|
54
55
|
!transfer_spec.key?('EX_ssh_key_paths')
|
55
|
-
# transfer_spec['remote_password'] = Installation.instance.bypass_pass # not used
|
56
|
+
# transfer_spec['remote_password'] = Installation.instance.bypass_pass # not used: no passphrase
|
56
57
|
transfer_spec['EX_ssh_key_paths'] = Installation.instance.bypass_keys
|
57
58
|
end
|
58
59
|
|
@@ -69,7 +70,7 @@ module Aspera
|
|
69
70
|
Log.log.error{"multi_session(#{transfer_spec['multi_session']}) shall be integer >= 0"}
|
70
71
|
multi_session_info = nil
|
71
72
|
elsif multi_session_info[:count].eql?(0)
|
72
|
-
Log.log.debug('multi_session count is zero: no
|
73
|
+
Log.log.debug('multi_session count is zero: no multi session')
|
73
74
|
multi_session_info = nil
|
74
75
|
elsif @options[:multi_incr_udp] # multi_session_info[:count] > 0
|
75
76
|
# if option not true: keep default udp port for all sessions
|
@@ -81,7 +82,7 @@ module Aspera
|
|
81
82
|
end
|
82
83
|
|
83
84
|
# compute known args
|
84
|
-
env_args = Parameters.ts_to_env_args(transfer_spec, wss: @options[:wss])
|
85
|
+
env_args = Parameters.ts_to_env_args(transfer_spec, wss: @options[:wss], ascp_args: @options[:ascp_args])
|
85
86
|
|
86
87
|
# add fallback cert and key as arguments if needed
|
87
88
|
if %w[1 force].include?(transfer_spec['http_fallback'])
|
@@ -99,11 +100,12 @@ module Aspera
|
|
99
100
|
|
100
101
|
# generic session information
|
101
102
|
session = {
|
102
|
-
thread:
|
103
|
-
error:
|
104
|
-
io:
|
105
|
-
id:
|
106
|
-
|
103
|
+
thread: nil, # Thread object monitoring management port, not nil when pushed to :sessions
|
104
|
+
error: nil, # exception if failed
|
105
|
+
io: nil, # management port server socket
|
106
|
+
id: nil, # SessionId from INIT message in mgt port
|
107
|
+
token_regenerator: token_regenerator, # regenerate bearer token with oauth
|
108
|
+
env_args: env_args # env vars and args to ascp (from transfer spec)
|
107
109
|
}
|
108
110
|
|
109
111
|
if multi_session_info.nil?
|
@@ -261,10 +263,10 @@ module Aspera
|
|
261
263
|
Log.log.error{"code: #{last_status_event['Code']}"}
|
262
264
|
if /bearer token/i.match?(last_status_event['Description'])
|
263
265
|
Log.log.error('need to regenerate token'.red)
|
264
|
-
if
|
266
|
+
if session[:token_regenerator].respond_to?(:refreshed_transfer_token)
|
265
267
|
# regenerate token here, expired, or error on it
|
266
268
|
# Note: in multi-session, each session will have a different one.
|
267
|
-
env_args[:env]['ASPERA_SCP_TOKEN'] =
|
269
|
+
env_args[:env]['ASPERA_SCP_TOKEN'] = session[:token_regenerator].refreshed_transfer_token
|
268
270
|
end
|
269
271
|
end
|
270
272
|
# cannot resolve address
|
@@ -327,8 +329,6 @@ module Aspera
|
|
327
329
|
session[:io].puts(command)
|
328
330
|
end
|
329
331
|
|
330
|
-
attr_writer :token_regenerator
|
331
|
-
|
332
332
|
private
|
333
333
|
|
334
334
|
# @param options : keys(symbol): see DEFAULT_OPTIONS
|
@@ -349,7 +349,6 @@ module Aspera
|
|
349
349
|
end
|
350
350
|
Log.log.debug{"local options= #{options}"}
|
351
351
|
@resume_policy = ResumePolicy.new(@options[:resume].symbolize_keys)
|
352
|
-
@token_regenerator = nil
|
353
352
|
end
|
354
353
|
|
355
354
|
# transfer thread entry
|
@@ -18,9 +18,10 @@ module Aspera
|
|
18
18
|
# message returned by HTTP GW in case of success
|
19
19
|
MSG_END_UPLOAD = 'end upload'
|
20
20
|
MSG_END_SLICE = 'end_slice_upload'
|
21
|
+
# options available in CLI (transfer_info)
|
21
22
|
DEFAULT_OPTIONS = {
|
22
23
|
url: nil,
|
23
|
-
|
24
|
+
upload_chunk_size: 64_000,
|
24
25
|
upload_bar_refresh_sec: 0.5
|
25
26
|
}.freeze
|
26
27
|
DEFAULT_BASE_PATH = '/aspera/http-gwy'
|
@@ -142,21 +143,21 @@ module Aspera
|
|
142
143
|
file_size = item['file_size']
|
143
144
|
file_name = File.basename(item[item['destination'].nil? ? 'source' : 'destination'])
|
144
145
|
# compute total number of slices
|
145
|
-
|
146
|
+
slice_total = ((file_size - 1) / @options[:upload_chunk_size]) + 1
|
146
147
|
File.open(source_paths[file_index]) do |file|
|
147
148
|
# current slice index
|
148
|
-
|
149
|
+
slice_index = 0
|
149
150
|
until file.eof?
|
150
|
-
data = file.read(@options[:
|
151
|
+
data = file.read(@options[:upload_chunk_size])
|
151
152
|
slice_data = {
|
152
153
|
name: file_name,
|
153
154
|
type: file_mime_type,
|
154
155
|
size: file_size,
|
155
|
-
slice:
|
156
|
-
total_slices:
|
156
|
+
slice: slice_index,
|
157
|
+
total_slices: slice_total,
|
157
158
|
fileIndex: file_index
|
158
159
|
}
|
159
|
-
# Log.dump(:slice_data,slice_data) #if
|
160
|
+
# Log.dump(:slice_data,slice_data) #if slice_index.eql?(0)
|
160
161
|
# interrupt main thread if read thread failed
|
161
162
|
raise shared_info[:read_exception] unless shared_info[:read_exception].nil?
|
162
163
|
begin
|
@@ -164,22 +165,22 @@ module Aspera
|
|
164
165
|
slice_data[:data] = Base64.strict_encode64(data)
|
165
166
|
ws_snd_json(slice_upload: slice_data)
|
166
167
|
else
|
167
|
-
ws_snd_json(slice_upload: slice_data) if
|
168
|
+
ws_snd_json(slice_upload: slice_data) if slice_index.eql?(0)
|
168
169
|
ws_send(data, type: :binary)
|
169
|
-
Log.log.debug{"ws: sent buffer: #{file_index} / #{
|
170
|
-
ws_snd_json(slice_upload: slice_data) if
|
170
|
+
Log.log.debug{"ws: sent buffer: #{file_index} / #{slice_index}"}
|
171
|
+
ws_snd_json(slice_upload: slice_data) if slice_index.eql?(slice_total - 1)
|
171
172
|
end
|
172
173
|
rescue Errno::EPIPE => e
|
173
174
|
raise shared_info[:read_exception] unless shared_info[:read_exception].nil?
|
174
175
|
raise e
|
175
176
|
end
|
176
177
|
sent_bytes += data.length
|
177
|
-
|
178
|
-
if last_progress_time.nil? || ((
|
178
|
+
current_time = Time.now
|
179
|
+
if last_progress_time.nil? || ((current_time - last_progress_time) > @options[:upload_bar_refresh_sec])
|
179
180
|
notify_progress(session_id, sent_bytes)
|
180
|
-
last_progress_time =
|
181
|
+
last_progress_time = current_time
|
181
182
|
end
|
182
|
-
|
183
|
+
slice_index += 1
|
183
184
|
end
|
184
185
|
end
|
185
186
|
file_index += 1
|
@@ -201,14 +202,14 @@ module Aspera
|
|
201
202
|
# is normally provided by application, like package name
|
202
203
|
if !transfer_spec.key?('download_name')
|
203
204
|
# by default it is the name of first file
|
204
|
-
|
205
|
+
download_name = File.basename(transfer_spec['paths'].first['source'])
|
205
206
|
# we remove extension
|
206
|
-
|
207
|
+
download_name = download_name.gsub(/\.@gw_api.*$/, '')
|
207
208
|
# ands add indication of number of files if there is more than one
|
208
209
|
if transfer_spec['paths'].length > 1
|
209
|
-
|
210
|
+
download_name += " #{transfer_spec['paths'].length} Files"
|
210
211
|
end
|
211
|
-
transfer_spec['download_name'] =
|
212
|
+
transfer_spec['download_name'] = download_name
|
212
213
|
end
|
213
214
|
creation = @gw_api.create('v1/download', {'transfer_spec' => transfer_spec})[:data]
|
214
215
|
transfer_uuid = creation['url'].split('/').last
|
@@ -227,7 +228,7 @@ module Aspera
|
|
227
228
|
# start FASP transfer based on transfer spec (hash table)
|
228
229
|
# note that it is asynchronous
|
229
230
|
# HTTP download only supports file list
|
230
|
-
def start_transfer(transfer_spec)
|
231
|
+
def start_transfer(transfer_spec, token_regenerator: nil)
|
231
232
|
raise 'GW URL must be set' if @gw_api.nil?
|
232
233
|
raise 'paths: must be Array' unless transfer_spec['paths'].is_a?(Array)
|
233
234
|
raise 'only token based transfer is supported in GW' unless transfer_spec['token'].is_a?(String)
|
@@ -23,8 +23,8 @@ module Aspera
|
|
23
23
|
rest_params = { base_url: options[:url]}
|
24
24
|
if /^Bearer /.match?(options[:password])
|
25
25
|
rest_params[:headers] = {
|
26
|
-
Aspera::Node::
|
27
|
-
'Authorization'
|
26
|
+
Aspera::Node::HEADER_X_ASPERA_ACCESS_KEY => options[:username],
|
27
|
+
'Authorization' => options[:password]
|
28
28
|
}
|
29
29
|
raise 'root_id is required for access key' if @root_id.nil?
|
30
30
|
else
|
@@ -56,7 +56,7 @@ module Aspera
|
|
56
56
|
end
|
57
57
|
|
58
58
|
# generic method
|
59
|
-
def start_transfer(transfer_spec)
|
59
|
+
def start_transfer(transfer_spec, token_regenerator: nil)
|
60
60
|
# add root id if access key
|
61
61
|
if !@root_id.nil?
|
62
62
|
case transfer_spec['direction']
|
@@ -79,7 +79,7 @@ module Aspera
|
|
79
79
|
if transfer_spec['tags'].is_a?(Hash) && transfer_spec['tags']['aspera'].is_a?(Hash)
|
80
80
|
transfer_spec['tags']['aspera']['xfer_retry'] ||= 150
|
81
81
|
end
|
82
|
-
#
|
82
|
+
# Optimization in case of sending to the same node (TODO: probably remove this, as /etc/hosts shall be used for that)
|
83
83
|
if !transfer_spec['wss_enabled'] && transfer_spec['remote_host'].eql?(URI.parse(node_api_.params[:base_url]).host)
|
84
84
|
transfer_spec['remote_host'] = '127.0.0.1'
|
85
85
|
end
|
@@ -96,8 +96,8 @@ module Aspera
|
|
96
96
|
# lets emulate management events to display progress bar
|
97
97
|
loop do
|
98
98
|
# status is empty sometimes with status 200...
|
99
|
-
|
100
|
-
case
|
99
|
+
transfer_data = node_api_.read("ops/transfers/#{@transfer_id}")[:data] || {'status' => 'unknown'} rescue {'status' => 'waiting(read error)'}
|
100
|
+
case transfer_data['status']
|
101
101
|
when 'completed'
|
102
102
|
notify_end(@transfer_id)
|
103
103
|
break
|
@@ -106,21 +106,19 @@ module Aspera
|
|
106
106
|
spinner = TTY::Spinner.new('[:spinner] :title', format: :classic)
|
107
107
|
spinner.start
|
108
108
|
end
|
109
|
-
spinner.update(title:
|
109
|
+
spinner.update(title: transfer_data['status'])
|
110
110
|
spinner.spin
|
111
|
-
# puts trdata
|
112
111
|
when 'running'
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
notify_begin(@transfer_id, trdata['precalc']['bytes_expected'])
|
112
|
+
if !started && transfer_data['precalc'].is_a?(Hash) &&
|
113
|
+
transfer_data['precalc']['status'].eql?('ready')
|
114
|
+
notify_begin(@transfer_id, transfer_data['precalc']['bytes_expected'])
|
117
115
|
started = true
|
118
116
|
else
|
119
|
-
notify_progress(@transfer_id,
|
117
|
+
notify_progress(@transfer_id, transfer_data['bytes_transferred'])
|
120
118
|
end
|
121
119
|
else
|
122
|
-
Log.log.warn{"
|
123
|
-
raise Fasp::Error, "#{
|
120
|
+
Log.log.warn{"transfer_data -> #{transfer_data}"}
|
121
|
+
raise Fasp::Error, "#{transfer_data['status']}: #{transfer_data['error_desc']}"
|
124
122
|
end
|
125
123
|
sleep(1)
|
126
124
|
end
|
@@ -58,7 +58,7 @@ module Aspera
|
|
58
58
|
end
|
59
59
|
end
|
60
60
|
|
61
|
-
def start_transfer(transfer_spec)
|
61
|
+
def start_transfer(transfer_spec, token_regenerator: nil)
|
62
62
|
# create a transfer request
|
63
63
|
transfer_request = Transfersdk::TransferRequest.new(
|
64
64
|
transferType: Transfersdk::TransferType::FILE_REGULAR, # transfer type (file/stream)
|
@@ -128,7 +128,7 @@ module Aspera
|
|
128
128
|
FILES = %i[ascp ascp4 ssh_bypass_key_dsa ssh_bypass_key_rsa aspera_license aspera_conf fallback_cert fallback_key].freeze
|
129
129
|
|
130
130
|
# get path of one resource file of currently activated product
|
131
|
-
# keys and certs are generated locally... (they are well known values, arch.
|
131
|
+
# keys and certs are generated locally... (they are well known values, arch. independent)
|
132
132
|
def path(k)
|
133
133
|
case k
|
134
134
|
when :ascp, :ascp4
|
@@ -182,7 +182,7 @@ module Aspera
|
|
182
182
|
# @return the file path of local connect where API's URI can be read
|
183
183
|
def connect_uri
|
184
184
|
connect = get_product_folders(PRODUCT_CONNECT)
|
185
|
-
folder = File.join(connect[:run_root],
|
185
|
+
folder = File.join(connect[:run_root], VAR_RUN_SUBFOLDER)
|
186
186
|
['', 's'].each do |ext|
|
187
187
|
uri_file = File.join(folder, "http#{ext}.uri")
|
188
188
|
Log.log.debug{"checking connect port file: #{uri_file}"}
|
@@ -264,7 +264,7 @@ module Aspera
|
|
264
264
|
end
|
265
265
|
end
|
266
266
|
File.unlink(sdk_zip_path) rescue nil # Windows may give error
|
267
|
-
# ensure license file are generated so that ascp
|
267
|
+
# ensure license file are generated so that ascp invocation for version works
|
268
268
|
path(:aspera_license)
|
269
269
|
path(:aspera_conf)
|
270
270
|
ascp_path = File.join(sdk_folder, ascp_filename)
|
@@ -285,13 +285,13 @@ module Aspera
|
|
285
285
|
|
286
286
|
BIN_SUBFOLDER = 'bin'
|
287
287
|
ETC_SUBFOLDER = 'etc'
|
288
|
-
|
288
|
+
VAR_RUN_SUBFOLDER = File.join('var', 'run')
|
289
289
|
# product information manifest: XML (part of aspera product)
|
290
290
|
PRODUCT_INFO = 'product-info.mf'
|
291
291
|
# policy for product selection
|
292
292
|
FIRST_FOUND = 'FIRST'
|
293
293
|
|
294
|
-
private_constant :BIN_SUBFOLDER, :ETC_SUBFOLDER, :
|
294
|
+
private_constant :BIN_SUBFOLDER, :ETC_SUBFOLDER, :VAR_RUN_SUBFOLDER, :PRODUCT_INFO
|
295
295
|
|
296
296
|
def initialize
|
297
297
|
@path_to_ascp = nil
|
data/lib/aspera/fasp/listener.rb
CHANGED
@@ -18,8 +18,9 @@ module Aspera
|
|
18
18
|
SUPPORTED_AGENTS = %i[direct node connect].freeze
|
19
19
|
# Short names of columns in manual
|
20
20
|
SUPPORTED_AGENTS_SHORT = SUPPORTED_AGENTS.map{|a|a.to_s[0].to_sym}
|
21
|
+
FILE_LIST_OPTIONS = ['--file-list', '--file-pair-list'].freeze
|
21
22
|
|
22
|
-
private_constant :SUPPORTED_AGENTS
|
23
|
+
private_constant :SUPPORTED_AGENTS, :FILE_LIST_OPTIONS
|
23
24
|
|
24
25
|
class << self
|
25
26
|
# Temp folder for file lists, must contain only file lists
|
@@ -40,34 +41,34 @@ module Aspera
|
|
40
41
|
# @return a table suitable to display in manual
|
41
42
|
def man_table(to_text: true)
|
42
43
|
result = []
|
43
|
-
description.each do |
|
44
|
-
param = {name:
|
44
|
+
description.each do |name, options|
|
45
|
+
param = {name: name, type: [options[:accepted_types]].flatten.join(','), description: options[:desc]}
|
45
46
|
# add flags for supported agents in doc
|
46
47
|
SUPPORTED_AGENTS.each do |a|
|
47
|
-
param[a.to_s[0].to_sym] =
|
48
|
+
param[a.to_s[0].to_sym] = options[:agents].nil? || options[:agents].include?(a) ? 'Y' : ''
|
48
49
|
end
|
49
50
|
# only keep lines that are usable in supported agents
|
50
51
|
next if SUPPORTED_AGENTS_SHORT.inject(true){|m, j|m && param[j].empty?}
|
51
52
|
param[:cli] =
|
52
|
-
case
|
53
|
-
when :envvar then 'env:' +
|
54
|
-
when :opt_without_arg then
|
53
|
+
case options[:cli][:type]
|
54
|
+
when :envvar then 'env:' + options[:cli][:variable]
|
55
|
+
when :opt_without_arg then options[:cli][:switch]
|
55
56
|
when :opt_with_arg
|
56
|
-
values = if
|
57
|
+
values = if options.key?(:enum)
|
57
58
|
['enum']
|
58
|
-
elsif
|
59
|
-
|
60
|
-
elsif !
|
61
|
-
[
|
59
|
+
elsif options[:accepted_types].is_a?(Array)
|
60
|
+
options[:accepted_types]
|
61
|
+
elsif !options[:accepted_types].nil?
|
62
|
+
[options[:accepted_types]]
|
62
63
|
else
|
63
64
|
raise "error: #{param}"
|
64
65
|
end.map{|n|"{#{n}}"}.join('|')
|
65
|
-
conv =
|
66
|
-
"#{
|
66
|
+
conv = options[:cli].key?(:convert) ? '(conversion)' : ''
|
67
|
+
"#{options[:cli][:switch]} #{conv}#{values}"
|
67
68
|
else ''
|
68
69
|
end
|
69
|
-
if
|
70
|
-
param[:description] += "\nAllowed values: #{
|
70
|
+
if options.key?(:enum)
|
71
|
+
param[:description] += "\nAllowed values: #{options[:enum].join(', ')}"
|
71
72
|
end
|
72
73
|
param[:description] = param[:description].gsub('/', '/') if to_text
|
73
74
|
result.push(param)
|
@@ -75,24 +76,27 @@ module Aspera
|
|
75
76
|
return result
|
76
77
|
end
|
77
78
|
|
78
|
-
# special encoding methods used in YAML (key: :
|
79
|
-
def
|
79
|
+
# special encoding methods used in YAML (key: :convert)
|
80
|
+
def convert_remove_hyphen(v); v.tr('-', ''); end
|
80
81
|
|
81
|
-
# special encoding methods used in YAML (key: :
|
82
|
-
def
|
82
|
+
# special encoding methods used in YAML (key: :convert)
|
83
|
+
def convert_json64(v); Base64.strict_encode64(JSON.generate(v)); end
|
83
84
|
|
84
|
-
# special encoding methods used in YAML (key: :
|
85
|
-
def
|
85
|
+
# special encoding methods used in YAML (key: :convert)
|
86
|
+
def convert_base64(v); Base64.strict_encode64(v); end
|
86
87
|
|
87
88
|
# file list is provided directly with ascp arguments
|
88
|
-
def ts_has_ascp_file_list(ts)
|
89
|
-
|
89
|
+
def ts_has_ascp_file_list(ts, ascp_args)
|
90
|
+
# it can also be option transfer_info
|
91
|
+
ascp_args = ascp_args['ascp_args'] if ascp_args.is_a?(Hash)
|
92
|
+
(ts['EX_ascp_args'].is_a?(Array) && ts['EX_ascp_args'].any?{|i|FILE_LIST_OPTIONS.include?(i)}) ||
|
93
|
+
(ascp_args.is_a?(Array) && ascp_args.any?{|i|FILE_LIST_OPTIONS.include?(i)}) ||
|
90
94
|
ts.key?('EX_file_list') ||
|
91
95
|
ts.key?('EX_file_pair_list')
|
92
96
|
end
|
93
97
|
|
94
|
-
def ts_to_env_args(transfer_spec,
|
95
|
-
return Parameters.new(transfer_spec,
|
98
|
+
def ts_to_env_args(transfer_spec, wss:, ascp_args:)
|
99
|
+
return Parameters.new(transfer_spec, wss: wss, ascp_args: ascp_args).ascp_args
|
96
100
|
end
|
97
101
|
|
98
102
|
# temp file list files are created here
|
@@ -107,29 +111,32 @@ module Aspera
|
|
107
111
|
attr_reader :file_list_folder
|
108
112
|
end # self
|
109
113
|
|
110
|
-
# @param options [Hash] key: :wss: bool
|
111
|
-
def initialize(job_spec,
|
114
|
+
# @param options [Hash] key: :wss: bool, :ascp_args: array of strings
|
115
|
+
def initialize(job_spec, wss:, ascp_args:)
|
112
116
|
@job_spec = job_spec
|
113
|
-
@
|
117
|
+
@opt_wss = wss
|
118
|
+
@opt_args = ascp_args
|
119
|
+
Log.log.debug{"agent options: #{@opt_wss} #{@opt_args}"}
|
120
|
+
raise 'ascp args must be an Array' unless @opt_args.is_a?(Array)
|
121
|
+
raise 'ascp args must be an Array of String' if @opt_args.any?{|i|!i.is_a?(String)}
|
114
122
|
@builder = Aspera::CommandLineBuilder.new(@job_spec, self.class.description)
|
115
|
-
Log.log.debug{"agent options: #{@options}"}
|
116
123
|
end
|
117
124
|
|
118
125
|
def process_file_list
|
119
126
|
# is the file list provided through EX_ parameters?
|
120
|
-
ascp_file_list_provided = self.class.ts_has_ascp_file_list(@job_spec)
|
127
|
+
ascp_file_list_provided = self.class.ts_has_ascp_file_list(@job_spec, @opt_args)
|
121
128
|
# set if paths is mandatory in ts
|
122
129
|
@builder.params_definition['paths'][:mandatory] = !@job_spec.key?('keepalive') && !ascp_file_list_provided
|
123
130
|
# get paths in transfer spec (after setting if it is mandatory)
|
124
|
-
ts_paths_array = @builder.
|
131
|
+
ts_paths_array = @builder.read_param('paths')
|
125
132
|
if ascp_file_list_provided && !ts_paths_array.nil?
|
126
133
|
raise 'file list provided both in transfer spec and ascp file list. Remove one of them.'
|
127
134
|
end
|
128
135
|
# option 1: EX_file_list
|
129
|
-
file_list_file = @builder.
|
136
|
+
file_list_file = @builder.read_param('EX_file_list')
|
130
137
|
if file_list_file.nil?
|
131
138
|
# option 2: EX_file_pair_list
|
132
|
-
file_list_file = @builder.
|
139
|
+
file_list_file = @builder.read_param('EX_file_pair_list')
|
133
140
|
if !file_list_file.nil?
|
134
141
|
option = '--file-pair-list'
|
135
142
|
elsif !ts_paths_array.nil?
|
@@ -140,7 +147,7 @@ module Aspera
|
|
140
147
|
@builder.add_command_line_options(ts_paths_array.map{|i|i['source']})
|
141
148
|
else
|
142
149
|
# safer option: generate a file list file if there is storage defined for it
|
143
|
-
# if there is destination in paths, then use
|
150
|
+
# if there is destination in paths, then use file-pair-list
|
144
151
|
# TODO: well, we test only the first one, but anyway it shall be consistent
|
145
152
|
if ts_paths_array.first.key?('destination')
|
146
153
|
option = '--file-pair-list'
|
@@ -179,12 +186,12 @@ module Aspera
|
|
179
186
|
@job_spec.delete('source_root') if @job_spec.key?('source_root') && @job_spec['source_root'].empty?
|
180
187
|
|
181
188
|
# use web socket session initiation ?
|
182
|
-
if @builder.
|
189
|
+
if @builder.read_param('wss_enabled') && (@opt_wss || !@job_spec.key?('fasp_port'))
|
183
190
|
# by default use web socket session if available, unless removed by user
|
184
191
|
@builder.add_command_line_options(['--ws-connect'])
|
185
192
|
# TODO: option to give order ssh,ws (legacy http is implied bu ssh)
|
186
|
-
#
|
187
|
-
@job_spec['ssh_port'] = @builder.
|
193
|
+
# This will need to be cleaned up in aspera core
|
194
|
+
@job_spec['ssh_port'] = @builder.read_param('wss_port')
|
188
195
|
@job_spec.delete('fasp_port')
|
189
196
|
@job_spec.delete('EX_ssh_key_paths')
|
190
197
|
@job_spec.delete('sshfp')
|
@@ -200,7 +207,7 @@ module Aspera
|
|
200
207
|
@builder.process_params
|
201
208
|
|
202
209
|
# symbol must be index of Installation.paths
|
203
|
-
if @builder.
|
210
|
+
if @builder.read_param('use_ascp4')
|
204
211
|
env_args[:ascp_version] = :ascp4
|
205
212
|
else
|
206
213
|
env_args[:ascp_version] = :ascp
|
@@ -210,9 +217,10 @@ module Aspera
|
|
210
217
|
# get list of files to transfer and build arg for ascp
|
211
218
|
process_file_list
|
212
219
|
# optional args, at the end to override previous ones (to allow override)
|
213
|
-
@builder.add_command_line_options(@builder.
|
220
|
+
@builder.add_command_line_options(@builder.read_param('EX_ascp_args'))
|
221
|
+
@builder.add_command_line_options(@opt_args)
|
214
222
|
# process destination folder
|
215
|
-
destination_folder = @builder.
|
223
|
+
destination_folder = @builder.read_param('destination_root') || '/'
|
216
224
|
# ascp4 does not support base64 encoding of destination
|
217
225
|
destination_folder = Base64.strict_encode64(destination_folder) unless env_args[:ascp_version].eql?(:ascp4)
|
218
226
|
# destination MUST be last command line argument to ascp
|