Class: Aspera::Cli::Main

Inherits:
Object
  • Object
show all
Defined in:
lib/aspera/cli/main.rb

Overview

The main CLI class

Constant Summary collapse

STATUS_FIELD =

store transfer result using this key and use result_transfer_multiple

'status'
SELF_SIGNED_CERT =

for testing only

OpenSSL::SSL.const_get(:enon_yfirev.to_s.upcase.reverse)

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.result_emptyObject

expect some list, but nothing to display



38
# File 'lib/aspera/cli/main.rb', line 38

def result_empty; return {type: :empty, data: :nil}; end

.result_nothingObject

nothing expected



41
# File 'lib/aspera/cli/main.rb', line 41

def result_nothing; return {type: :nothing, data: :nil}; end

.result_status(status) ⇒ Object



43
# File 'lib/aspera/cli/main.rb', line 43

def result_status(status); return {type: :status, data: status}; end

.result_successObject



45
# File 'lib/aspera/cli/main.rb', line 45

def result_success; return result_status('complete'); end

.result_transfer(statuses) ⇒ Object

Process statuses of finished transfer sessions raise exception if there is one error else returns an empty status



50
51
52
53
54
# File 'lib/aspera/cli/main.rb', line 50

def result_transfer(statuses)
  worst = TransferAgent.session_status(statuses)
  raise worst unless worst.eql?(:success)
  return Main.result_nothing
end

.result_transfer_multiple(status_table) ⇒ Object

used when one command executes several transfer jobs (each job being possibly multi session) each element has a key STATUS_FIELD which contains the result of possibly multiple sessions

Parameters:

  • status_table (Array)
    array],…,…

Returns:

  • a status object suitable as command result



60
61
62
63
64
65
66
67
68
69
70
# File 'lib/aspera/cli/main.rb', line 60

def result_transfer_multiple(status_table)
  global_status = :success
  # transform status array into string and find if there was problem
  status_table.each do |item|
    worst = TransferAgent.session_status(item[STATUS_FIELD])
    global_status = worst unless worst.eql?(:success)
    item[STATUS_FIELD] = item[STATUS_FIELD].map(&:to_s).join(',')
  end
  raise global_status unless global_status.eql?(:success)
  return {type: :object_list, data: status_table}
end

Instance Method Details

#process_command_lineObject

this is the main function called by initial script just after constructor



276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
# File 'lib/aspera/cli/main.rb', line 276

def process_command_line
  Log.log.debug('process_command_line')
  # catch exception information , if any
  exception_info = nil
  # false if command shall not be executed ("once_only")
  execute_command = true
  begin
    # find plugins, shall be after parse! ?
    @plugin_env[:config].add_plugins_from_lookup_folders
    # help requested without command ? (plugins must be known here)
    exit_with_usage(true) if @option_help && @opt_mgr.command_or_arg_empty?
    generate_bash_completion if @bash_completion
    @plugin_env[:config].periodic_check_newer_gem_version
    command_sym =
      if @option_show_config && @opt_mgr.command_or_arg_empty?
        Plugins::Config::CONF_PLUGIN_SYM
      else
        @opt_mgr.get_next_command(@plugin_env[:config].plugins.keys.dup.unshift(:help))
      end
    # command will not be executed, but we need manual
    @opt_mgr.fail_on_missing_mandatory = false if @option_help || @option_show_config
    # main plugin is not dynamically instantiated
    case command_sym
    when :help
      exit_with_usage(true)
    when Plugins::Config::CONF_PLUGIN_SYM
      command_plugin = @plugin_env[:config]
    else
      # get plugin, set options, etc
      command_plugin = get_plugin_instance_with_options(command_sym)
      # parse plugin specific options
      @opt_mgr.parse_options!
    end
    # help requested for current plugin
    exit_with_usage(false) if @option_help
    if @option_show_config
      @formatter.display_results({type: :single_object, data: @opt_mgr.declared_options(only_defined: true)})
      execute_command = false
    end
    # locking for single execution (only after "per plugin" option, in case lock port is there)
    lock_port = @opt_mgr.get_option(:lock_port)
    if !lock_port.nil?
      begin
        # no need to close later, will be freed on process exit. must save in member else it is garbage collected
        Log.log.debug{"Opening lock port #{lock_port.to_i}"}
        @tcp_server = TCPServer.new('127.0.0.1', lock_port.to_i)
      rescue StandardError => e
        execute_command = false
        Log.log.warn{"Another instance is already running (#{e.message})."}
      end
    end
    # execute and display (if not exclusive execution)
    @formatter.display_results(command_plugin.execute_action) if execute_command
    # finish
    @plugin_env[:transfer].shutdown
  rescue Net::SSH::AuthenticationFailed => e; exception_info = {e: e, t: 'SSH', security: true}
  rescue OpenSSL::SSL::SSLError => e;         exception_info = {e: e, t: 'SSL'}
  rescue CliBadArgument => e;                 exception_info = {e: e, t: 'Argument', usage: true}
  rescue CliNoSuchId => e;                    exception_info = {e: e, t: 'Identifier'}
  rescue CliError => e;                       exception_info = {e: e, t: 'Tool', usage: true}
  rescue Fasp::Error => e;                    exception_info = {e: e, t: 'FASP(ascp)'}
  rescue Aspera::RestCallError => e;          exception_info = {e: e, t: 'Rest'}
  rescue SocketError => e;                    exception_info = {e: e, t: 'Network'}
  rescue StandardError => e;                  exception_info = {e: e, t: 'Other', debug: true}
  rescue Interrupt => e;                      exception_info = {e: e, t: 'Interruption', debug: true}
  end
  # cleanup file list files
  TempFileManager.instance.cleanup
  # 1- processing of error condition
  unless exception_info.nil?
    Log.log.warn(exception_info[:e].message) if Aspera::Log.instance.logger_type.eql?(:syslog) && exception_info[:security]
    @formatter.display_message(:error, "#{ERROR_FLASH} #{exception_info[:t]}: #{exception_info[:e].message}")
    @formatter.display_message(:error, 'Use option -h to get help.') if exception_info[:usage]
    # Provide hint on FASP errors
    if exception_info[:e].is_a?(Fasp::Error) && exception_info[:e].message.eql?('Remote host is not who we expected')
      @formatter.display_message(:error, "For this specific error, refer to:\n"\
        "#{SRC_URL}#error-remote-host-is-not-who-we-expected\nAdd this to arguments:\n--ts=@json:'{\"sshfp\":null}'")
    end
    # Provide hint on SSL errors
    if exception_info[:e].is_a?(OpenSSL::SSL::SSLError) && ['does not match the server certificate'].any?{|m|exception_info[:e].message.include?(m)}
      @formatter.display_message(:error, "You can ignore SSL errors with option:\n--insecure=yes")
    end
  end
  # 2- processing of command not processed (due to exception or bad command line)
  if execute_command || @option_show_config
    @opt_mgr.final_errors.each do |msg|
      @formatter.display_message(:error, "#{ERROR_FLASH} Argument: #{msg}")
      # add code as exception if there is not already an error
      exception_info = {e: Exception.new(msg), t: 'UnusedArg'} if exception_info.nil?
    end
  end
  # 3- in case of error, fail the process status
  unless exception_info.nil?
    # show stack trace in debug mode
    raise exception_info[:e] if Log.instance.level.eql?(:debug)
    # else give hint and exit
    @formatter.display_message(:error, 'Use --log-level=debug to get more details.') if exception_info[:debug]
    Process.exit(1)
  end
  return nil
end