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.
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