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 =

Plugins store transfer result using this key and use result_transfer_multiple()

'status'

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.result_emptyObject

expect some list, but nothing to display



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

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

.result_image(blob, formatter:) ⇒ Object



63
64
65
# File 'lib/aspera/cli/main.rb', line 63

def result_image(blob, formatter:)
  return Main.result_status(formatter.status_image(blob))
end

.result_nothingObject

nothing expected



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

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

.result_status(status) ⇒ Object



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

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

.result_successObject



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

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



41
42
43
44
45
# File 'lib/aspera/cli/main.rb', line 41

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



51
52
53
54
55
56
57
58
59
60
61
# File 'lib/aspera/cli/main.rb', line 51

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



241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
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
# File 'lib/aspera/cli/main.rb', line 241

def process_command_line
  # catch exception information , if any
  exception_info = nil
  # false if command shall not be executed (e.g. --show-config)
  execute_command = true
  # catch exceptions
  begin
    init_agents_and_options
    # find plugins, shall be after parse! ?
    PluginFactory.instance.add_plugins_from_lookup_folders
    # help requested without command ? (plugins must be known here)
    exit_with_usage(true) if @option_help && options.command_or_arg_empty?
    generate_bash_completion if @bash_completion
    config.periodic_check_newer_gem_version
    command_sym =
      if @option_show_config && options.command_or_arg_empty?
        COMMAND_CONFIG
      else
        options.get_next_command(PluginFactory.instance.plugin_list.unshift(COMMAND_HELP))
      end
    # command will not be executed, but we need manual
    options.fail_on_missing_mandatory = false if @option_help || @option_show_config
    # main plugin is not dynamically instantiated
    case command_sym
    when COMMAND_HELP
      exit_with_usage(true)
    when COMMAND_CONFIG
      command_plugin = config
    else
      # get plugin, set options, etc
      command_plugin = get_plugin_instance_with_options(command_sym)
      # parse plugin specific options
      options.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: options.known_options(only_defined: true).stringify_keys)
      execute_command = false
    end
    # locking for single execution (only after "per plugin" option, in case lock port is there)
    lock_port = options.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}"}
        @tcp_server = TCPServer.new('127.0.0.1', lock_port)
      rescue StandardError => e
        execute_command = false
        Log.log.warn{"Another instance is already running (#{e.message})."}
      end
    end
    pid_file = options.get_option(:pid_file)
    if !pid_file.nil?
      File.write(pid_file, Process.pid)
      Log.log.debug{"Wrote pid #{Process.pid} to #{pid_file}"}
      at_exit{File.delete(pid_file)}
    end
    # execute and display (if not exclusive execution)
    formatter.display_results(**command_plugin.execute_action) if execute_command
    # save config file if command modified it
    config.save_config_file_if_needed
    # finish
    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 Cli::BadArgument => e;               exception_info = {e: e, t: 'Argument', usage: true}
  rescue Cli::NoSuchIdentifier => e;          exception_info = {e: e, t: 'Identifier'}
  rescue Cli::Error => e;                     exception_info = {e: e, t: 'Tool', usage: true}
  rescue Transfer::Error => e;                exception_info = {e: e, t: 'Transfer'}
  rescue 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(#{e.class.name})", 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 Log.instance.logger_type.eql?(:syslog) && exception_info[:security]
    formatter.display_message(:error, "#{Formatter::ERROR_FLASH} #{exception_info[:t]}: #{exception_info[:e].message}")
    formatter.display_message(:error, 'Use option -h to get help.') if exception_info[:usage]
    # Is that a known error condition with proposal for remediation ?
    Hints.hint_for(exception_info[:e], formatter)
  end
  # 2- processing of command not processed (due to exception or bad command line)
  if execute_command || @option_show_config
    options.final_errors.each do |msg|
      formatter.display_message(:error, "#{Formatter::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.log.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