aspera-cli 4.11.0 → 4.12.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.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/BUGS.md +0 -1
  4. data/CHANGELOG.md +71 -52
  5. data/CONTRIBUTING.md +31 -6
  6. data/README.md +404 -259
  7. data/bin/asession +2 -2
  8. data/docs/test_env.conf +1 -0
  9. data/examples/aoc.rb +2 -2
  10. data/examples/dascli +11 -11
  11. data/examples/faspex4.rb +7 -7
  12. data/examples/node.rb +1 -1
  13. data/examples/proxy.pac +2 -2
  14. data/examples/server.rb +3 -3
  15. data/lib/aspera/aoc.rb +105 -40
  16. data/lib/aspera/cli/extended_value.rb +4 -4
  17. data/lib/aspera/cli/{formater.rb → formatter.rb} +7 -7
  18. data/lib/aspera/cli/listener/progress.rb +1 -1
  19. data/lib/aspera/cli/listener/progress_multi.rb +2 -2
  20. data/lib/aspera/cli/main.rb +18 -18
  21. data/lib/aspera/cli/manager.rb +5 -5
  22. data/lib/aspera/cli/plugin.rb +23 -20
  23. data/lib/aspera/cli/plugins/aoc.rb +75 -112
  24. data/lib/aspera/cli/plugins/ats.rb +6 -6
  25. data/lib/aspera/cli/plugins/config.rb +84 -83
  26. data/lib/aspera/cli/plugins/cos.rb +1 -1
  27. data/lib/aspera/cli/plugins/faspex.rb +38 -38
  28. data/lib/aspera/cli/plugins/faspex5.rb +187 -43
  29. data/lib/aspera/cli/plugins/node.rb +30 -37
  30. data/lib/aspera/cli/plugins/orchestrator.rb +7 -4
  31. data/lib/aspera/cli/plugins/preview.rb +10 -9
  32. data/lib/aspera/cli/plugins/server.rb +1 -1
  33. data/lib/aspera/cli/plugins/shares.rb +67 -43
  34. data/lib/aspera/cli/transfer_agent.rb +16 -16
  35. data/lib/aspera/cli/version.rb +2 -1
  36. data/lib/aspera/command_line_builder.rb +70 -66
  37. data/lib/aspera/cos_node.rb +9 -9
  38. data/lib/aspera/fasp/agent_base.rb +3 -1
  39. data/lib/aspera/fasp/agent_connect.rb +23 -23
  40. data/lib/aspera/fasp/agent_direct.rb +13 -14
  41. data/lib/aspera/fasp/agent_httpgw.rb +20 -19
  42. data/lib/aspera/fasp/agent_node.rb +13 -15
  43. data/lib/aspera/fasp/agent_trsdk.rb +1 -1
  44. data/lib/aspera/fasp/installation.rb +5 -5
  45. data/lib/aspera/fasp/listener.rb +1 -1
  46. data/lib/aspera/fasp/parameters.rb +49 -41
  47. data/lib/aspera/fasp/parameters.yaml +311 -212
  48. data/lib/aspera/fasp/resume_policy.rb +2 -2
  49. data/lib/aspera/fasp/transfer_spec.rb +0 -13
  50. data/lib/aspera/faspex_gw.rb +80 -161
  51. data/lib/aspera/faspex_postproc.rb +77 -0
  52. data/lib/aspera/log.rb +7 -7
  53. data/lib/aspera/nagios.rb +6 -6
  54. data/lib/aspera/node.rb +24 -19
  55. data/lib/aspera/oauth.rb +50 -47
  56. data/lib/aspera/proxy_auto_config.js +22 -22
  57. data/lib/aspera/proxy_auto_config.rb +3 -3
  58. data/lib/aspera/rest.rb +12 -10
  59. data/lib/aspera/rest_error_analyzer.rb +5 -5
  60. data/lib/aspera/secret_hider.rb +4 -3
  61. data/lib/aspera/ssh.rb +4 -4
  62. data/lib/aspera/sync.rb +37 -36
  63. data/lib/aspera/web_auth.rb +7 -59
  64. data/lib/aspera/web_server_simple.rb +76 -0
  65. data.tar.gz.sig +0 -0
  66. metadata +6 -4
  67. 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
- MAX_CONNECT_START_RETRY = 4
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 :MAX_CONNECT_START_RETRY, :SLEEP_SEC_BETWEEN_RETRY
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
- trynumber = 0
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
- cinfo = @connect_api.read('info/version')[:data]
27
- Log.log.info('Connect was reached') if trynumber > 0
28
- Log.dump(:connect_version, cinfo)
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
- raise StandardError, "Unable to start connect #{trynumber} times" if trynumber >= MAX_CONNECT_START_RETRY
31
- Log.log.warn{"Aspera Connect is not started (#{e}). Trying to start it ##{trynumber}..."}
32
- trynumber += 1
33
- start_url = trynumber <= 2 ? 'fasp://initialize' : 'https://test-connect.ibmaspera.com/'
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
- resdata = @connect_api.create('windows/select-open-file-dialog/', {
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'] = resdata['dataTransfer']['files'].map { |i| {'source' => i['name']}}
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
- trdata = tr_info['transfer_info']
81
- if trdata.nil?
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 trdata['status']
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: trdata['status'])
95
+ spinner.update(title: transfer['status'])
96
96
  spinner.spin
97
97
  when 'running'
98
- # puts "running: sessions:#{trdata['sessions'].length}, #{trdata['sessions'].map{|i| i['bytes_transferred']}.join(',')}"
99
- if !started && (trdata['bytes_expected'] != 0)
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'], trdata['bytes_expected'])
101
+ notify_begin(@connect_settings['app_id'], transfer['bytes_expected'])
102
102
  started = true
103
103
  else
104
- notify_progress(@connect_settings['app_id'], trdata['bytes_written'])
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, trdata['error_desc']
108
+ raise Fasp::Error, transfer['error_desc']
109
109
  else
110
- raise Fasp::Error, "unknown status: #{trdata['status']}: #{trdata['error_desc']}"
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 multisession')
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: nil, # Thread object monitoring management port, not nil when pushed to :sessions
103
- error: nil, # exception if failed
104
- io: nil, # management port server socket
105
- id: nil, # SessionId from INIT message in mgt port
106
- env_args: env_args # env vars and args to ascp (from transfer spec)
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 !@token_regenerator.nil?
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'] = @token_regenerator.call(true)
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
- upload_chunksize: 64_000,
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
- numslices = ((file_size - 1) / @options[:upload_chunksize]) + 1
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
- slicenum = 0
149
+ slice_index = 0
149
150
  until file.eof?
150
- data = file.read(@options[:upload_chunksize])
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: slicenum,
156
- total_slices: numslices,
156
+ slice: slice_index,
157
+ total_slices: slice_total,
157
158
  fileIndex: file_index
158
159
  }
159
- # Log.dump(:slice_data,slice_data) #if slicenum.eql?(0)
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 slicenum.eql?(0)
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} / #{slicenum}"}
170
- ws_snd_json(slice_upload: slice_data) if slicenum.eql?(numslices - 1)
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
- currenttime = Time.now
178
- if last_progress_time.nil? || ((currenttime - last_progress_time) > @options[:upload_bar_refresh_sec])
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 = currenttime
181
+ last_progress_time = current_time
181
182
  end
182
- slicenum += 1
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
- dname = File.basename(transfer_spec['paths'].first['source'])
205
+ download_name = File.basename(transfer_spec['paths'].first['source'])
205
206
  # we remove extension
206
- dname = dname.gsub(/\.@gw_api.*$/, '')
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
- dname += " #{transfer_spec['paths'].length} Files"
210
+ download_name += " #{transfer_spec['paths'].length} Files"
210
211
  end
211
- transfer_spec['download_name'] = dname
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::X_ASPERA_ACCESSKEY => options[:username],
27
- 'Authorization' => options[:password]
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
- # Optimisation in case of sending to the same node (TODO: probably remove this, as /etc/hosts shall be used for that)
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
- trdata = node_api_.read("ops/transfers/#{@transfer_id}")[:data] || {'status' => 'unknown'} rescue {'status' => 'waiting(read error)'}
100
- case trdata['status']
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: trdata['status'])
109
+ spinner.update(title: transfer_data['status'])
110
110
  spinner.spin
111
- # puts trdata
112
111
  when 'running'
113
- # puts "running: sessions:#{trdata["sessions"].length}, #{trdata["sessions"].map{|i| i['bytes_transferred']}.join(',')}"
114
- if !started && trdata['precalc'].is_a?(Hash) &&
115
- trdata['precalc']['status'].eql?('ready')
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, trdata['bytes_transferred'])
117
+ notify_progress(@transfer_id, transfer_data['bytes_transferred'])
120
118
  end
121
119
  else
122
- Log.log.warn{"trdata -> #{trdata}"}
123
- raise Fasp::Error, "#{trdata['status']}: #{trdata['error_desc']}"
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. independant)
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], VARRUN_SUBFOLDER)
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 invokation for version works
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
- VARRUN_SUBFOLDER = File.join('var', 'run')
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, :VARRUN_SUBFOLDER, :PRODUCT_INFO
294
+ private_constant :BIN_SUBFOLDER, :ETC_SUBFOLDER, :VAR_RUN_SUBFOLDER, :PRODUCT_INFO
295
295
 
296
296
  def initialize
297
297
  @path_to_ascp = nil
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Aspera
4
4
  module Fasp
5
- # imlement this class to get transfer events
5
+ # implement this class to get transfer events
6
6
  class Listener # rubocop:disable Lint/EmptyClass
7
7
  # define one of the following methods:
8
8
  # event_text(text_data)
@@ -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 |k, i|
44
- param = {name: k, type: [i[:accepted_types]].flatten.join(','), description: i[:desc]}
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] = i[:tragents].nil? || i[:tragents].include?(a) ? 'Y' : ''
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 i[:cltype]
53
- when :envvar then 'env:' + i[:clvarname]
54
- when :opt_without_arg then i[:clswitch]
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 i.key?(:enum)
57
+ values = if options.key?(:enum)
57
58
  ['enum']
58
- elsif i[:accepted_types].is_a?(Array)
59
- i[:accepted_types]
60
- elsif !i[:accepted_types].nil?
61
- [i[:accepted_types]]
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 = i.key?(:clconvert) ? '(conversion)' : ''
66
- "#{i[:clswitch]} #{conv}#{values}"
66
+ conv = options[:cli].key?(:convert) ? '(conversion)' : ''
67
+ "#{options[:cli][:switch]} #{conv}#{values}"
67
68
  else ''
68
69
  end
69
- if i.key?(:enum)
70
- param[:description] += "\nAllowed values: #{i[:enum].join(', ')}"
70
+ if options.key?(:enum)
71
+ param[:description] += "\nAllowed values: #{options[:enum].join(', ')}"
71
72
  end
72
73
  param[:description] = param[:description].gsub('&sol;', '/') 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: :clconvert)
79
- def clconv_remove_hyphen(v); v.tr('-', ''); end
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: :clconvert)
82
- def clconv_json64(v); Base64.strict_encode64(JSON.generate(v)); end
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: :clconvert)
85
- def clconv_base64(v); Base64.strict_encode64(v); end
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
- (ts['EX_ascp_args'].is_a?(Array) && ['--file-list', '--file-pair-list'].any?{|i|ts['EX_ascp_args'].include?(i)}) ||
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, options)
95
- return Parameters.new(transfer_spec, options).ascp_args
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, options)
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
- @options = options
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.process_param('paths', :get_value)
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.process_param('EX_file_list', :get_value)
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.process_param('EX_file_pair_list', :get_value)
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 filepairlist
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.process_param('wss_enabled', :get_value) && (@options[:wss] || !@job_spec.key?('fasp_port'))
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
- # quel bordel:
187
- @job_spec['ssh_port'] = @builder.process_param('wss_port', :get_value)
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.process_param('use_ascp4', :get_value)
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.process_param('EX_ascp_args', :get_value))
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.process_param('destination_root', :get_value) || '/'
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