aspera-cli 4.18.1 → 4.19.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +13 -0
  4. data/CONTRIBUTING.md +5 -12
  5. data/README.md +60 -29
  6. data/examples/build_exec +85 -0
  7. data/lib/aspera/agent/base.rb +2 -0
  8. data/lib/aspera/agent/direct.rb +108 -104
  9. data/lib/aspera/api/aoc.rb +2 -2
  10. data/lib/aspera/api/httpgw.rb +91 -56
  11. data/lib/aspera/ascp/installation.rb +47 -32
  12. data/lib/aspera/ascp/management.rb +4 -1
  13. data/lib/aspera/ascp/products.rb +1 -7
  14. data/lib/aspera/cli/formatter.rb +24 -18
  15. data/lib/aspera/cli/manager.rb +10 -10
  16. data/lib/aspera/cli/plugin.rb +2 -2
  17. data/lib/aspera/cli/plugin_factory.rb +10 -1
  18. data/lib/aspera/cli/plugins/config.rb +15 -10
  19. data/lib/aspera/cli/plugins/node.rb +4 -3
  20. data/lib/aspera/cli/plugins/server.rb +1 -1
  21. data/lib/aspera/cli/plugins/shares.rb +11 -7
  22. data/lib/aspera/cli/sync_actions.rb +72 -31
  23. data/lib/aspera/cli/transfer_agent.rb +1 -0
  24. data/lib/aspera/cli/transfer_progress.rb +1 -1
  25. data/lib/aspera/cli/version.rb +1 -1
  26. data/lib/aspera/environment.rb +43 -10
  27. data/lib/aspera/faspex_gw.rb +1 -1
  28. data/lib/aspera/keychain/encrypted_hash.rb +2 -0
  29. data/lib/aspera/log.rb +1 -0
  30. data/lib/aspera/node_simulator.rb +1 -1
  31. data/lib/aspera/oauth/jwt.rb +1 -1
  32. data/lib/aspera/oauth/url_json.rb +2 -0
  33. data/lib/aspera/oauth/web.rb +5 -4
  34. data/lib/aspera/secret_hider.rb +3 -2
  35. data/lib/aspera/ssh.rb +1 -1
  36. data/lib/aspera/transfer/faux_file.rb +7 -5
  37. data/lib/aspera/transfer/parameters.rb +27 -19
  38. data/lib/aspera/transfer/spec.rb +8 -10
  39. data/lib/aspera/transfer/sync.rb +52 -47
  40. data/lib/aspera/web_auth.rb +0 -1
  41. data/lib/aspera/web_server_simple.rb +24 -13
  42. data.tar.gz.sig +0 -0
  43. metadata +3 -3
  44. metadata.gz.sig +0 -0
  45. data/examples/rubyc +0 -24
@@ -41,8 +41,11 @@ module Aspera
41
41
  </CONF>
42
42
  END_OF_CONFIG_FILE
43
43
  # all ascp files (in SDK)
44
- FILES = %i[ascp ascp4 transferd ssh_private_dsa ssh_private_rsa aspera_license aspera_conf fallback_certificate fallback_private_key].freeze
44
+ EXE_FILES = %i[ascp ascp4 async].freeze
45
+ FILES = %i[transferd ssh_private_dsa ssh_private_rsa aspera_license aspera_conf fallback_certificate fallback_private_key].unshift(*EXE_FILES).freeze
45
46
  private_constant :EXT_RUBY_PROTOBUF, :RB_SDK_FOLDER, :DEFAULT_ASPERA_CONF, :FILES
47
+ # options for SSH client private key
48
+ CLIENT_SSH_KEY_OPTIONS = %i{dsa_rsa rsa per_client}.freeze
46
49
  # set ascp executable path
47
50
  def ascp_path=(v)
48
51
  @path_to_ascp = v
@@ -110,14 +113,14 @@ module Aspera
110
113
  def path(k)
111
114
  file_is_optional = false
112
115
  case k
113
- when :ascp, :ascp4
116
+ when *EXE_FILES
117
+ file_is_optional = k.eql?(:async)
114
118
  use_ascp_from_product(FIRST_FOUND) if @path_to_ascp.nil?
115
- file = @path_to_ascp
116
119
  # NOTE: that there might be a .exe at the end
117
- file = file.gsub('ascp', 'ascp4') if k.eql?(:ascp4)
120
+ file = @path_to_ascp.gsub('ascp', k.to_s)
118
121
  when :transferd
119
- file = transferd_filepath
120
122
  file_is_optional = true
123
+ file = transferd_filepath
121
124
  when :ssh_private_dsa, :ssh_private_rsa
122
125
  # assume last 3 letters are type
123
126
  type = k.to_s[-3..-1].to_sym
@@ -151,8 +154,16 @@ module Aspera
151
154
  return DataRepository.instance.item(:uuid)
152
155
  end
153
156
 
154
- def aspera_token_ssh_key_paths
155
- return %i[ssh_private_dsa ssh_private_rsa].map{|i|Installation.instance.path(i)}
157
+ # get paths of SSH keys to use for ascp client
158
+ # @param types [Symbol] types to use
159
+ def aspera_token_ssh_key_paths(types)
160
+ Aspera.assert_values(types, CLIENT_SSH_KEY_OPTIONS)
161
+ return case types
162
+ when :dsa_rsa, :rsa
163
+ types.to_s.split('_').map{|i|Installation.instance.path("ssh_private_#{i}".to_sym)}
164
+ when :per_client
165
+ raise 'Not yet implemented'
166
+ end
156
167
  end
157
168
 
158
169
  # use in plugin `config`
@@ -169,13 +180,14 @@ module Aspera
169
180
  raise "An error occurred when testing #{ascp_filename}: #{cmd_out}" unless $CHILD_STATUS == 0
170
181
  # get version from ascp, only after full extract, as windows requires DLLs (SSL/TLS/etc...)
171
182
  m = cmd_out.match(/ version ([0-9.]+)/)
172
- exe_version = m[1] unless m.nil?
183
+ exe_version = m[1].gsub(/\.$/, '') unless m.nil?
173
184
  return exe_version
174
185
  end
175
186
 
176
- def ascp_add_pvcl(data)
187
+ def ascp_pvcl_info
188
+ data = {}
177
189
  # read PATHs from ascp directly, and pvcl modules as well
178
- Open3.popen3(data['ascp'], '-DDL-') do |_stdin, _stdout, stderr, thread|
190
+ Open3.popen3(ascp_path, '-DDL-') do |_stdin, _stdout, stderr, thread|
179
191
  last_line = ''
180
192
  while (line = stderr.gets)
181
193
  line.chomp!
@@ -194,32 +206,32 @@ module Aspera
194
206
  data['product_name'] = Regexp.last_match(1)
195
207
  data['product_version'] = Regexp.last_match(2)
196
208
  when /^LOG Initializing FASP version ([^,]+),/
197
- data['ascp_version'] = Regexp.last_match(1)
209
+ data['sdk_ascp_version'] = Regexp.last_match(1)
198
210
  end
199
211
  end
200
212
  if !thread.value.exitstatus.eql?(1) && !data.key?('root')
201
213
  raise last_line
202
214
  end
203
215
  end
216
+ return data
204
217
  end
205
218
 
206
219
  # extract some stings from ascp binary
207
- def ascp_add_openssl(data)
208
- ascp_file = data['ascp']
209
- File.binread(ascp_file).scan(/[\x20-\x7E]{10,}/) do |bin_string|
220
+ def ascp_ssl_info
221
+ data = {}
222
+ File.binread(ascp_path).scan(/[\x20-\x7E]{10,}/) do |bin_string|
210
223
  if (m = bin_string.match(/OPENSSLDIR.*"(.*)"/))
211
224
  data['openssldir'] = m[1]
212
225
  elsif (m = bin_string.match(/OpenSSL (\d[^ -]+)/))
213
226
  data['openssl_version'] = m[1]
214
227
  end
215
- end if File.file?(ascp_file)
228
+ end if File.file?(ascp_path)
229
+ return data
216
230
  end
217
231
 
218
232
  def ascp_info
219
- data = file_paths
220
- ascp_add_pvcl(data)
221
- ascp_add_openssl(data)
222
- return data
233
+ files = file_paths
234
+ return files.merge(ascp_pvcl_info).merge(ascp_ssl_info)
223
235
  end
224
236
 
225
237
  # download aspera SDK or use local file
@@ -263,19 +275,22 @@ module Aspera
263
275
  # ensure license file are generated so that ascp invocation for version works
264
276
  path(:aspera_license)
265
277
  path(:aspera_conf)
266
- ascp_file = Products.ascp_filename
267
- ascp_path = File.join(sdk_folder, ascp_file)
268
- raise "No #{ascp_file} found in SDK archive" unless File.exist?(ascp_path)
269
- Environment.restrict_file_access(ascp_path, mode: 0o755)
270
- Environment.restrict_file_access(ascp_path.gsub('ascp', 'ascp4'), mode: 0o755)
271
- ascp_version = get_ascp_version(ascp_path)
272
- trd_path = transferd_filepath
273
- Log.log.warn{"No #{trd_path} in SDK archive"} unless File.exist?(trd_path)
274
- Environment.restrict_file_access(trd_path, mode: 0o755) if File.exist?(trd_path)
275
- transferd_version = get_exe_version(trd_path, 'version')
276
- sdk_version = transferd_version || ascp_version
277
- File.write(File.join(sdk_folder, Products::INFO_META_FILE), "<product><name>IBM Aspera SDK</name><version>#{sdk_version}</version></product>")
278
- return sdk_version
278
+ sdk_ascp_file = Products.ascp_filename
279
+ sdk_ascp_path = File.join(sdk_folder, sdk_ascp_file)
280
+ raise "No #{sdk_ascp_file} found in SDK archive" unless File.exist?(sdk_ascp_path)
281
+ EXE_FILES.each do |exe_sym|
282
+ exe_path = sdk_ascp_path.gsub('ascp', exe_sym.to_s)
283
+ Environment.restrict_file_access(exe_path, mode: 0o755) if File.exist?(exe_path)
284
+ end
285
+ sdk_ascp_version = get_ascp_version(sdk_ascp_path)
286
+ sdk_daemon_path = transferd_filepath
287
+ Log.log.warn{"No #{sdk_daemon_path} in SDK archive"} unless File.exist?(sdk_daemon_path)
288
+ Environment.restrict_file_access(sdk_daemon_path, mode: 0o755) if File.exist?(sdk_daemon_path)
289
+ transferd_version = get_exe_version(sdk_daemon_path, 'version')
290
+ sdk_name = 'IBM Aspera Transfer SDK'
291
+ sdk_version = transferd_version || sdk_ascp_version
292
+ File.write(File.join(sdk_folder, Products::INFO_META_FILE), "<product><name>#{sdk_name}</name><version>#{sdk_version}</version></product>")
293
+ return sdk_name, sdk_version
279
294
  end
280
295
 
281
296
  private
@@ -188,7 +188,7 @@ module Aspera
188
188
  # empty line is separator to end event information
189
189
  MGT_FRAME_SEPARATOR = ''
190
190
  # fields description for JSON generation
191
- # spellchecker: disable
191
+ # cspell: disable
192
192
  INTEGER_FIELDS = %w[Bytescont FaspFileArgIndex StartByte Rate MinRate Port Priority RateCap MinRateCap TCPPort CreatePolicy TimePolicy
193
193
  DatagramSize XoptFlags VLinkVersion PeerVLinkVersion DSPipelineDepth PeerDSPipelineDepth ReadBlockSize WriteBlockSize
194
194
  ClusterNumNodes ClusterNodeId Size Written Loss FileBytes PreTransferBytes TransferBytes PMTU Elapsedusec ArgScansAttempted
@@ -219,6 +219,9 @@ module Aspera
219
219
  end
220
220
  attr_reader :last_event
221
221
 
222
+ # process line of mgt port event
223
+ # @param line [String] line of mgt port event
224
+ # @return [Hash] event hash or nil if event is not yet complete
222
225
  def process_line(line)
223
226
  # Log.log.debug{"line=[#{line}]"}
224
227
  case line
@@ -126,7 +126,7 @@ module Aspera
126
126
 
127
127
  # filename for ascp with optional extension (Windows)
128
128
  def ascp_filename
129
- return 'ascp' + Environment.exe_extension
129
+ return "ascp#{Environment.exe_extension}"
130
130
  end
131
131
 
132
132
  # @return folder paths for specified applications
@@ -150,12 +150,6 @@ module Aspera
150
150
  end
151
151
  raise "no connect uri file found in #{folder}"
152
152
  end
153
-
154
- # @ return path to configuration file of aspera CLI
155
- # def cli_conf_file
156
- # connect = folders(PRODUCT_CLI_V1)
157
- # return File.join(connect[:app_root], BIN_SUBFOLDER, '.aspera_cli_conf')
158
- # end
159
153
  end
160
154
  end
161
155
  end
@@ -98,11 +98,12 @@ module Aspera
98
98
  CSV_RECORD_SEPARATOR = "\n"
99
99
  CSV_FIELD_SEPARATOR = ','
100
100
  # supported output formats
101
- DISPLAY_FORMATS = %i[text nagios ruby json jsonpp yaml table csv image].freeze
101
+ DISPLAY_FORMATS = %i[text nagios ruby json jsonpp yaml table multi csv image].freeze
102
102
  # user output levels
103
103
  DISPLAY_LEVELS = %i[info data error].freeze
104
+ FIELD_VALUE_HEADINGS = %i[key value].freeze
104
105
 
105
- private_constant :DISPLAY_FORMATS, :DISPLAY_LEVELS, :CSV_RECORD_SEPARATOR, :CSV_FIELD_SEPARATOR
106
+ private_constant :DISPLAY_FORMATS, :DISPLAY_LEVELS, :CSV_RECORD_SEPARATOR, :CSV_FIELD_SEPARATOR, :FIELD_VALUE_HEADINGS
106
107
  # prefix to display error messages in user messages (terminal)
107
108
  ERROR_FLASH = 'ERROR:'.bg_red.gray.blink.freeze
108
109
  WARNING_FLASH = 'WARNING:'.bg_brown.black.blink.freeze
@@ -116,7 +117,7 @@ module Aspera
116
117
 
117
118
  def tick(yes)
118
119
  result =
119
- if Environment.terminal_supports_unicode?
120
+ if Environment.instance.terminal_supports_unicode?
120
121
  if yes
121
122
  "\u2713"
122
123
  else
@@ -150,16 +151,9 @@ module Aspera
150
151
  end
151
152
 
152
153
  # Highlight special values
153
- def special_format(what, use_colors: $stdout.isatty)
154
- result = $stdout.isatty ? "<#{what}>" : "&lt;#{what}&gt;"
155
- if use_colors
156
- result = if %w[null empty].any?{|s|what.include?(s)}
157
- result.dim
158
- else
159
- result.reverse_color
160
- end
161
- end
162
- return result
154
+ def special_format(what)
155
+ result = "<#{what}>"
156
+ return %w[null empty].any?{|s|what.include?(s)} ? result.dim : result.reverse_color
163
157
  end
164
158
 
165
159
  # call this after REST calls if several api calls are expected
@@ -198,7 +192,7 @@ module Aspera
198
192
  end
199
193
 
200
194
  def declare_options(options)
201
- default_table_style = if Environment.terminal_supports_unicode?
195
+ default_table_style = if Environment.instance.terminal_supports_unicode?
202
196
  {border: :unicode_round}
203
197
  else
204
198
  {}
@@ -326,16 +320,16 @@ module Aspera
326
320
  display_message(:info, special_format('empty')) if @options[:format].eql?(:table)
327
321
  return
328
322
  end
323
+ # if table has only one element, and only one field, display the value
329
324
  if object_array.length == 1 && fields.length == 1
330
325
  display_message(:data, object_array.first[fields.first])
331
326
  return
332
327
  end
333
328
  # Special case if only one row (it could be object_list or single_object)
334
329
  if @options[:transpose_single] && object_array.length == 1
335
- new_columns = %i[key value]
336
330
  single = object_array.first
337
- object_array = fields.map { |i| new_columns.zip([i, single[i]]).to_h }
338
- fields = new_columns
331
+ object_array = fields.map { |i| FIELD_VALUE_HEADINGS.zip([i, single[i]]).to_h }
332
+ fields = FIELD_VALUE_HEADINGS
339
333
  end
340
334
  Log.log.debug{Log.dump(:object_array, object_array)}
341
335
  # convert data to string, and keep only display fields
@@ -348,8 +342,18 @@ module Aspera
348
342
  headings: fields,
349
343
  rows: final_table_rows,
350
344
  style: @options[:table_style]&.symbolize_keys))
345
+ when :multi
346
+ final_table_rows.each do |row|
347
+ Log.log.debug{Log.dump(:row, row)}
348
+ display_message(:data, Terminal::Table.new(
349
+ headings: FIELD_VALUE_HEADINGS,
350
+ rows: fields.zip(row),
351
+ style: @options[:table_style]&.symbolize_keys))
352
+ end
351
353
  when :csv
352
354
  display_message(:data, final_table_rows.map{|t| t.join(CSV_FIELD_SEPARATOR)}.join(CSV_RECORD_SEPARATOR))
355
+ else
356
+ raise "not expected: #{@options[:format]}"
353
357
  end
354
358
  end
355
359
 
@@ -420,7 +424,7 @@ module Aspera
420
424
  end
421
425
  raise "not url: #{url.class} #{url}" unless url.is_a?(String)
422
426
  display_message(:data, status_image(url))
423
- when :table, :csv
427
+ when :table, :csv, :multi
424
428
  case type
425
429
  when :config_over
426
430
  display_table(Flattener.new(self).config_over(data), CONF_OVERVIEW_KEYS)
@@ -452,6 +456,8 @@ module Aspera
452
456
  else
453
457
  raise "unknown data type: #{type}"
454
458
  end
459
+ else
460
+ raise "not expected: #{@options[:format]}"
455
461
  end
456
462
  end
457
463
  end
@@ -20,7 +20,7 @@ module Aspera
20
20
  @method = method_name
21
21
  @option_name = option_name
22
22
  @has_writer = @object.respond_to?(writer_method)
23
- Log.log.debug{"AttrAccessor: #{@option_name}: #{@object.class}.#{@method}: writer=#{@has_writer}"}
23
+ Log.log.trace1{"AttrAccessor: #{@option_name}: #{@object.class}.#{@method}: writer=#{@has_writer}"}
24
24
  Aspera.assert(@object.respond_to?(@method)) {"#{object} does not respond to #{method_name}"}
25
25
  end
26
26
 
@@ -328,7 +328,7 @@ module Aspera
328
328
  if opt[:read_write].eql?(:accessor)
329
329
  Aspera.assert_type(handler, Hash)
330
330
  Aspera.assert(handler.keys.sort.eql?(%i[m o]))
331
- Log.log.debug{"set attr obj #{option_symbol} (#{handler[:o]},#{handler[:m]})"}
331
+ Log.log.trace1{"set attr obj: #{option_symbol} (#{handler[:o]},#{handler[:m]})"}
332
332
  opt[:accessor] = AttrAccessor.new(handler[:o], handler[:m], option_symbol)
333
333
  end
334
334
  set_option(option_symbol, default, where: 'default') unless default.nil?
@@ -372,7 +372,7 @@ module Aspera
372
372
  @parser.on(*on_args, &block)
373
373
  else Aspera.error_unexpected_value(values)
374
374
  end
375
- Log.log.debug{"on_args=#{on_args}"}
375
+ Log.log.trace1{"on_args=#{on_args}"}
376
376
  end
377
377
 
378
378
  # Adds each of the keys of specified hash as an option
@@ -435,7 +435,7 @@ module Aspera
435
435
 
436
436
  # removes already known options from the list
437
437
  def parse_options!
438
- Log.log.debug('parse_options!'.red)
438
+ Log.log.trace1('parse_options!'.red)
439
439
  # first conf file, then env var
440
440
  consume_option_pairs(@option_pairs_batch, 'set')
441
441
  consume_option_pairs(@option_pairs_env, 'env')
@@ -443,16 +443,16 @@ module Aspera
443
443
  unknown_options = []
444
444
  begin
445
445
  # remove known options one by one, exception if unknown
446
- Log.log.debug('before parse'.red)
446
+ Log.log.trace1('before parse'.red)
447
447
  @parser.parse!(@unprocessed_cmd_line_options)
448
- Log.log.debug('After parse'.red)
448
+ Log.log.trace1('After parse'.red)
449
449
  rescue OptionParser::InvalidOption => e
450
- Log.log.debug{"InvalidOption #{e}".red}
450
+ Log.log.trace1{"InvalidOption #{e}".red}
451
451
  # save for later processing
452
452
  unknown_options.push(e.args.first)
453
453
  retry
454
454
  end
455
- Log.log.debug{"remains: #{unknown_options}"}
455
+ Log.log.trace1{"remains: #{unknown_options}"}
456
456
  # set unprocessed options for next time
457
457
  @unprocessed_cmd_line_options = unknown_options
458
458
  end
@@ -538,7 +538,7 @@ module Aspera
538
538
  # @param unprocessed_options [Array] list of options to apply (key_sym,value)
539
539
  # @param where [String] where the options come from
540
540
  def consume_option_pairs(unprocessed_options, where)
541
- Log.log.debug{"consume_option_pairs: #{where}"}
541
+ Log.log.trace1{"consume_option_pairs: #{where}"}
542
542
  options_to_set = {}
543
543
  unprocessed_options.each do |k, v|
544
544
  if @declared_options.key?(k)
@@ -548,7 +548,7 @@ module Aspera
548
548
  end
549
549
  options_to_set[k] = v
550
550
  else
551
- Log.log.debug{"unprocessed: #{k}: #{v}"}
551
+ Log.log.trace1{"unprocessed: #{k}: #{v}"}
552
552
  end
553
553
  end
554
554
  options_to_set.each do |k, v|
@@ -5,7 +5,7 @@ require 'aspera/assert'
5
5
 
6
6
  module Aspera
7
7
  module Cli
8
- # base class for plugins modules
8
+ # Base class for plugins
9
9
  class Plugin
10
10
  # operations without id
11
11
  GLOBAL_OPS = %i[create list].freeze
@@ -53,8 +53,8 @@ module Aspera
53
53
  options.parser.separator('OPTIONS:')
54
54
  end
55
55
 
56
+ # @return a hash of instance variables
56
57
  def init_params
57
- # return a hash of instance variables
58
58
  INIT_PARAMS.map{|p| [p, instance_variable_get("@#{p}".to_sym)]}.to_h
59
59
  end
60
60
 
@@ -3,7 +3,7 @@
3
3
  require 'singleton'
4
4
  module Aspera
5
5
  module Cli
6
- # option is retrieved from another object using accessor
6
+ # Instantiate plugin from well-known locations
7
7
  class PluginFactory
8
8
  include Singleton
9
9
 
@@ -19,14 +19,17 @@ module Aspera
19
19
  @plugins = {}
20
20
  end
21
21
 
22
+ # @return list of registered plugins
22
23
  def plugin_list
23
24
  @plugins.keys
24
25
  end
25
26
 
27
+ # @return path to source file of plugin
26
28
  def plugin_source(plugin_name_sym)
27
29
  @plugins[plugin_name_sym][:source]
28
30
  end
29
31
 
32
+ # add a folder to the list of folders to look for plugins
30
33
  def add_lookup_folder(folder)
31
34
  @lookup_folders.unshift(folder)
32
35
  end
@@ -43,6 +46,7 @@ module Aspera
43
46
  end
44
47
  end
45
48
 
49
+ # @return Class object for plugin
46
50
  def plugin_class(plugin_name_sym)
47
51
  raise "ERROR: plugin not found: #{plugin_name_sym}" unless @plugins.key?(plugin_name_sym)
48
52
  require @plugins[plugin_name_sym][:require_stanza]
@@ -50,6 +54,9 @@ module Aspera
50
54
  return Object.const_get("#{Module.nesting[1]}::#{PLUGINS_MODULE}::#{plugin_name_sym.to_s.capitalize}")
51
55
  end
52
56
 
57
+ # Create specified plugin
58
+ # @param plugin_name_sym [Symbol] name of plugin
59
+ # @param args [Hash] arguments to pass to plugin constructor
53
60
  def create(plugin_name_sym, **args)
54
61
  # TODO: check that ancestor is Plugin?
55
62
  plugin_class(plugin_name_sym).new(**args)
@@ -57,6 +64,8 @@ module Aspera
57
64
 
58
65
  private
59
66
 
67
+ # add plugin information to list
68
+ # @param path [String] path to plugin source file
60
69
  def add_plugin_info(path)
61
70
  raise "ERROR: plugin path must end with #{RUBY_FILE_EXT}" if !path.end_with?(RUBY_FILE_EXT)
62
71
  plugin_symbol = File.basename(path, RUBY_FILE_EXT).to_sym
@@ -281,6 +281,8 @@ module Aspera
281
281
  attr_reader :option_ignore_cert_host_port, :progress_bar
282
282
 
283
283
  # add files, folders or default locations to the certificate store
284
+ # @param path_list [Array<String>] list of paths to add
285
+ # @return the list of paths
284
286
  def trusted_cert_locations=(path_list)
285
287
  path_list = [path_list] unless path_list.is_a?(Array)
286
288
  Aspera.assert_type(path_list, Array){'cert locations'}
@@ -296,10 +298,10 @@ module Aspera
296
298
  Log.log.debug{"Adding cert location: #{path}"}
297
299
  if path.eql?(SpecialValues::DEF)
298
300
  @certificate_store.set_default_paths
299
- paths_to_add = [
300
- OpenSSL::X509::DEFAULT_CERT_DIR,
301
- OpenSSL::X509::DEFAULT_CERT_FILE
302
- ].select{|f|File.exist?(f)}
301
+ paths_to_add = [OpenSSL::X509::DEFAULT_CERT_DIR]
302
+ # JRuby cert file seems not to be PEM
303
+ paths_to_add.push(OpenSSL::X509::DEFAULT_CERT_FILE) unless defined?(JRUBY_VERSION)
304
+ paths_to_add.select!{|f|File.exist?(f)}
303
305
  elsif File.file?(path)
304
306
  @certificate_store.add_file(path)
305
307
  elsif File.directory?(path)
@@ -578,7 +580,7 @@ module Aspera
578
580
  @config_checksum_on_disk = config_checksum
579
581
  end
580
582
  files_to_copy = []
581
- Log.log.debug{Log.dump('Available_presets', @config_presets)}
583
+ Log.log.trace1{Log.dump('Available_presets', @config_presets)}
582
584
  Aspera.assert_type(@config_presets, Hash){'config file YAML'}
583
585
  # check there is at least the config section
584
586
  Aspera.assert(@config_presets.key?(CONF_PRESET_CONFIG)){"Cannot find key: #{CONF_PRESET_CONFIG}"}
@@ -699,8 +701,7 @@ module Aspera
699
701
  return execute_connect_action
700
702
  when :use
701
703
  ascp_path = options.get_next_argument('path to ascp')
702
- ascp_version = Ascp::Installation.instance.get_ascp_version(ascp_path)
703
- formatter.display_status("ascp version: #{ascp_version}")
704
+ formatter.display_status("ascp version: #{Ascp::Installation.instance.get_ascp_version(ascp_path)}")
704
705
  set_global_default(:ascp_path, ascp_path)
705
706
  return Main.result_nothing
706
707
  when :show
@@ -729,8 +730,8 @@ module Aspera
729
730
  when :install
730
731
  # reset to default location, if older default was used
731
732
  Ascp::Installation.instance.sdk_folder = self.class.default_app_main_folder(app_name: APP_NAME_SDK) if @sdk_default_location
732
- v = Ascp::Installation.instance.install_sdk(options.get_option(:sdk_url, mandatory: true))
733
- return Main.result_status("Installed version #{v}")
733
+ n, v = Ascp::Installation.instance.install_sdk(options.get_option(:sdk_url, mandatory: true))
734
+ return Main.result_status("Installed #{n} version #{v}")
734
735
  when :spec
735
736
  return {
736
737
  type: :object_list,
@@ -871,7 +872,9 @@ module Aspera
871
872
  check_update
872
873
  initdemo
873
874
  vault
874
- throw].freeze
875
+ throw
876
+ platform
877
+ ].freeze
875
878
 
876
879
  # Main action procedure for plugin
877
880
  def execute_action
@@ -1015,6 +1018,8 @@ module Aspera
1015
1018
  exception_class = Object.const_get(exception_class_name)
1016
1019
  Aspera.assert(exception_class <= Exception){"#{exception_class} is not an exception: #{exception_class.class}"}
1017
1020
  raise exception_class, exception_text
1021
+ when :platform
1022
+ return Main.result_status(Environment.architecture)
1018
1023
  else Aspera.error_unreachable_line
1019
1024
  end
1020
1025
  end
@@ -531,16 +531,17 @@ module Aspera
531
531
  # if a single file: split into folder and path
532
532
  apifid = @api_node.resolve_api_fid(top_file_id, source_folder)
533
533
  if source_paths.empty?
534
+ # get precise info in this element
534
535
  file_info = apifid[:api].read("files/#{apifid[:file_id]}")[:data]
535
536
  case file_info['type']
536
537
  when 'file'
537
538
  # if the single source is a file, we need to split into folder path and filename
538
539
  src_dir_elements = source_folder.split(Api::Node::PATH_SEPARATOR)
539
- # filename is the last one
540
+ # filename is the last one, source folder is what remains
540
541
  source_paths = [{'source' => src_dir_elements.pop}]
541
- # source folder is what remains
542
+ # add trailing / so that link is resolved, if it's a shared folder
543
+ src_dir_elements.push('')
542
544
  source_folder = src_dir_elements.join(Api::Node::PATH_SEPARATOR)
543
- # TODO: instead of creating a new object, use the same, and change file id with parent folder id ? possible ?
544
545
  apifid = @api_node.resolve_api_fid(top_file_id, source_folder)
545
546
  when 'link', 'folder'
546
547
  # single source is 'folder' or 'link'
@@ -176,7 +176,7 @@ module Aspera
176
176
  def execute_transfer(command, transfer_spec)
177
177
  case command
178
178
  when :upload, :download
179
- Transfer::Spec.action_to_direction(transfer_spec, command)
179
+ transfer_spec['direction'] = Transfer::Spec.transfer_type_to_direction(command)
180
180
  return Main.result_transfer(transfer.start(transfer_spec))
181
181
  when :sync
182
182
  # lets ignore the arguments provided by execute_sync_action, we just give the transfer spec
@@ -7,7 +7,10 @@ module Aspera
7
7
  module Plugins
8
8
  # Plugin for Aspera Shares v1
9
9
  class Shares < Cli::BasicAuthPlugin
10
- NODE_API_PREFIX = 'node_api'
10
+ # path for node API after base url
11
+ NODE_API_PATH = 'node_api'
12
+ # path for node admin after base url
13
+ ADMIN_API_PATH = 'api/v1'
11
14
  class << self
12
15
  def detect(address_or_url)
13
16
  address_or_url = "https://#{address_or_url}" unless address_or_url.match?(%r{^[a-z]{1,6}://})
@@ -16,7 +19,7 @@ module Aspera
16
19
  begin
17
20
  # shall fail: shares requires auth, but we check error message
18
21
  # TODO: use ping instead ?
19
- api.read("#{NODE_API_PREFIX}/app")
22
+ api.read("#{NODE_API_PATH}/app")
20
23
  rescue RestCallError => e
21
24
  if e.response.code.to_s.eql?('401') && e.response.body.eql?('{"error":{"user_message":"API user authentication failed"}}')
22
25
  found = true
@@ -65,7 +68,7 @@ module Aspera
65
68
  nagios = Nagios.new
66
69
  begin
67
70
  res = Rest
68
- .new(base_url: "#{options.get_option(:url, mandatory: true)}/#{NODE_API_PREFIX}")
71
+ .new(base_url: "#{options.get_option(:url, mandatory: true)}/#{NODE_API_PATH}")
69
72
  .call(
70
73
  operation: 'GET',
71
74
  subpath: 'ping',
@@ -77,13 +80,13 @@ module Aspera
77
80
  end
78
81
  return nagios.result
79
82
  when :repository, :files
80
- api_shares_node = basic_auth_api(NODE_API_PREFIX)
83
+ api_shares_node = basic_auth_api(NODE_API_PATH)
81
84
  repo_command = options.get_next_command(Node::COMMANDS_SHARES)
82
85
  return Node
83
86
  .new(**init_params, api: api_shares_node)
84
87
  .execute_action(repo_command)
85
88
  when :admin
86
- api_shares_admin = basic_auth_api('api/v1')
89
+ api_shares_admin = basic_auth_api(ADMIN_API_PATH)
87
90
  admin_command = options.get_next_command(%i[node share transfer_settings user group].freeze)
88
91
  case admin_command
89
92
  when :node
@@ -92,8 +95,9 @@ module Aspera
92
95
  share_command = options.get_next_command(%i[user_permissions group_permissions].concat(Plugin::ALL_OPS))
93
96
  case share_command
94
97
  when *Plugin::ALL_OPS
95
- return entity_command(share_command, api_shares_admin, 'data/shares')
96
- # return {type: :object_list, data: all_shares, fields: %w[id name status status_message]}
98
+ return entity_command(
99
+ share_command, api_shares_admin, 'data/shares',
100
+ display_fields: %w[id name node_id directory percent_free])
97
101
  when :user_permissions, :group_permissions
98
102
  share_id = instance_identifier
99
103
  return entity_action(api_shares_admin, "data/shares/#{share_id}/#{share_command}")