Class: Aspera::Cli::Plugins::Config

Inherits:
Base
  • Object
show all
Includes:
AscpActions, GemChecker, Aspera::Cli::PresetActions, SyncActions, TransferActions, VaultManager
Defined in:
lib/aspera/cli/plugins/config.rb

Overview

Manage the CLI config file

Constant Summary

Constants included from Aspera::Cli::PresetActions

Aspera::Cli::PresetActions::CONF_OVERVIEW_KEYS, Aspera::Cli::PresetActions::GLOBAL_DEFAULT_KEYWORD

Constants included from AscpActions

AscpActions::TRANSFERD_APP_NAME

Constants included from GemChecker

GemChecker::GEM_CHECK_DATE_FMT

Constants included from SyncActions

SyncActions::ADMIN_COMMANDS, SyncActions::PATH_AND_INFO_ARGS, SyncActions::STATE_STR

Constants inherited from Base

Base::FILTER_ARGS

Instance Attribute Summary collapse

Attributes inherited from Base

#context, #help_path

Class Method Summary collapse

Instance Method Summary collapse

Methods included from TransferActions

#action_transfer_cleanup, #action_transfer_list, #action_transfer_status

Methods included from Aspera::Cli::PresetActions

#action_preset_ask, #action_preset_delete, #action_preset_get, #action_preset_initialize, #action_preset_list, #action_preset_lookup, #action_preset_overview, #action_preset_secure, #action_preset_set, #action_preset_show, #action_preset_unset, #action_preset_update

Methods included from AscpActions

#action_agents_list, #action_agents_parameters, #action_agents_show, #action_ascp_errors, #action_ascp_info, #action_ascp_install, #action_ascp_products_list, #action_ascp_schema, #action_ascp_show, #action_ascp_spec, #action_transferd_install, #action_transferd_list, #install_transfer_sdk, #set_sdk_dir

Methods included from GemChecker

#check_gem_version, #periodic_check_newer_gem_version

Methods included from VaultManager

#action_vault_create, #action_vault_delete, #action_vault_import, #action_vault_password, #action_vault_show, #vault, #vault_required, #vault_value

Methods included from SyncActions

#action_sync_admin_counters, #action_sync_admin_file_info, #action_sync_admin_find, #action_sync_admin_meta, #action_sync_admin_overview, #action_sync_admin_query, #action_sync_admin_status, #async_info_from_args, #db_from_args, included, register_sync_admin_commands, #run_sync_transfer

Methods inherited from Base

#action_for, #add_manual_header, application_name, #bulk_result, command, command_registry, commands_under, #config, crud_commands, declare_options, define_action_method, #dispatch_child, #dispatch_from_registry, #dispatch_leaf, #entity_create, #entity_delete, entity_display_name, #entity_list, #entity_modify, #entity_res_path, #entity_show, #execute_action, #execute_leaf, file_matcher, #formatter, #generate_help, #http_config, #invoke_action, option, #options, #persistency, #presets, #progress_bar, #query_read_delete, #resolve_argument, root_setup, #transfer, use_options, used_option_sources

Constructor Details

#initialize(**_) ⇒ Config

Returns a new instance of Config.



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/aspera/cli/plugins/config.rb', line 97

def initialize(**_)
  super
  @vault_instance = nil
  @sdk_default_location = false
  @option_cache_tokens = true
  # :no_default uses a &block callback - must stay imperative
  options.declare(:no_default, description: 'Do not load default configuration for plugin', allowed: Type::NONE, short: 'N') { presets.use_plugin_defaults = false }
  # Declare wizard options (Wizard#initialize calls options.declare internally)
  @wizard = Wizard.new(self, context.main_folder)
  options.parse_options!
  set_sdk_dir
  # HTTP options: declare metadata (class method), then bind to the instance
  Http.declare_options(options)
  context.http_config.bind_options(options)
  options.parse_options!
end

Instance Attribute Details

#gem_urlObject (readonly)

Returns the value of attribute gem_url.



353
354
355
# File 'lib/aspera/cli/plugins/config.rb', line 353

def gem_url
  @gem_url
end

#option_cache_tokensObject

Returns the value of attribute option_cache_tokens.



351
352
353
# File 'lib/aspera/cli/plugins/config.rb', line 351

def option_cache_tokens
  @option_cache_tokens
end

Class Method Details

.deep_clone(val) ⇒ Object

Deep clone hash so that it does not get modified in case of display and secret hide



64
65
66
# File 'lib/aspera/cli/plugins/config.rb', line 64

def deep_clone(val)
  return Marshal.load(Marshal.dump(val))
end

.default_app_main_folder(app_name:) ⇒ String

Returns Product config folder (~/.aspera/).

Returns:

  • (String)

    Product config folder (~/.aspera/)



76
77
78
79
80
# File 'lib/aspera/cli/plugins/config.rb', line 76

def default_app_main_folder(app_name:)
  Aspera.assert_type(app_name, String)
  Aspera.assert(!app_name.empty?, 'app_name must not be empty')
  return File.join(module_family_folder, app_name)
end

.gem_plugins_folderObject

Folder containing plugins in the gem's main folder



52
53
54
# File 'lib/aspera/cli/plugins/config.rb', line 52

def gem_plugins_folder
  File.dirname(File.expand_path(__FILE__))
end

.gem_src_rootString

Go up as many times as englobing modules (not counting class, as it is a file)

Returns:

  • (String)

    main folder where code is, i.e. .../lib



58
59
60
61
# File 'lib/aspera/cli/plugins/config.rb', line 58

def gem_src_root
  # Module.nesting[2] is Cli::Plugins
  File.expand_path(Module.nesting[2].to_s.gsub('::', '/').gsub(%r{[^/]+}, '..'), gem_plugins_folder)
end

.module_family_folderString

Returns product family folder (~/.aspera).

Returns:

  • (String)

    product family folder (~/.aspera)



69
70
71
72
73
# File 'lib/aspera/cli/plugins/config.rb', line 69

def module_family_folder
  user_home_folder = Dir.home
  Aspera.assert(Dir.exist?(user_home_folder), type: Cli::Error) { "Home folder does not exist: #{user_home_folder}. Check your user environment." }
  return File.join(user_home_folder, ASPERA_HOME_FOLDER_NAME)
end

Instance Method Details

#action_commandsObject



519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
# File 'lib/aspera/cli/plugins/config.rb', line 519

def action_commands
  commands = Plugins::Factory.instance.plugin_list.flat_map do |name|
    plugin_class = Plugins::Factory.instance.plugin_class(name)
    reg = plugin_class.command_registry
    reg.all_paths.reject { |path| reg.children_of(path).any? }.map do |path|
      spec = reg[path]
      # Build syntax by interleaving each path segment with the arguments declared on that node
      tokens = [name.to_s]
      path.each_with_index do |seg, i|
        tokens << seg.to_s
        node_args = reg[path[0, i + 1]]&.arguments.to_a
        node_args.each do |a|
          token = a.allowed ? a.allowed.join('|') : a.name.to_s
          token += '...' if a.multiple
          tokens << (a.mandatory ? "<#{token}>" : "[<#{token}>]")
        end
      end
      syntax = tokens.join(' ')
      {
        syntax:      syntax,
        description: spec&.description.to_s
      }
    end
  end
  Result::ObjectList.new(commands, fields: %w[syntax description])
end

#action_completion_bash(words: nil) ⇒ Object



567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
# File 'lib/aspera/cli/plugins/config.rb', line 567

def action_completion_bash(words: nil, **)
  if words.nil? || words.empty?
    # Level 0: propose plugin names
    Plugins::Factory.instance.plugin_list.each { |p| puts p }
  else
    plugin_sym = words.first.to_sym
    plugin_class = begin
      Plugins::Factory.instance.plugin_class(plugin_sym)
    rescue StandardError
      Process.exit(0)
    end
    # Navigate into the registry using remaining words as path
    path = words[1..].map(&:to_sym)
    plugin_class.command_registry.children_of(path).each_key { |k| puts k }
  end
  Process.exit(0)
end

#action_detect(url:, plugin_name: nil) ⇒ Object



483
484
485
486
487
# File 'lib/aspera/cli/plugins/config.rb', line 483

def action_detect(url:, plugin_name: nil, **)
  options.ask_missing_mandatory = true
  apps = @wizard.identify_plugins_for_url(url: url, plugin_name: plugin_name).freeze
  Result::ObjectList.new(apps)
end

#action_documentation(location: :github, section: nil) ⇒ Object

DSL handlers - one method per leaf command



380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
# File 'lib/aspera/cli/plugins/config.rb', line 380

def action_documentation(location: :github, section: nil, **)
  case location
  when :github
    section = "##{section}" unless section.nil?
    Environment.instance.open_uri("#{Info::DOC_URL}#{section}")
    return Result::Nothing.new
  when :toc, :local
    require 'aspera/markdown'
    local_doc = File.join(self.class.gem_src_root, '..', 'docs', 'README.md')
    Aspera.assert(File.exist?(local_doc), type: Cli::Error) { "Local documentation not found: #{local_doc}" }
    content = File.read(local_doc)
    if location == :toc
      entries = Markdown.toc(content)
      return Result::ObjectList.new(entries, fields: %w[level title anchor])
    end
    # :local
    if section
      text = Markdown.extract_section(content, section)
      Aspera.assert(!text.nil?, type: Cli::Error) { "Section not found: #{section}" }
      return Result::Text.new(text)
    end
    if Environment.instance.url_method.eql?(:graphical)
      Environment.instance.open_uri("file://#{local_doc}")
      return Result::Nothing.new
    end
    return Result::Text.new(content)
  end
end

#action_download(file_url:, file_dest: nil) ⇒ Object



432
433
434
435
436
437
438
# File 'lib/aspera/cli/plugins/config.rb', line 432

def action_download(file_url:, file_dest: nil, **)
  file_url = file_url.chomp
  file_dest = File.join(transfer.destination_folder(Transfer::Spec::DIRECTION_RECEIVE), file_url.gsub(%r{.*/}, '')) if file_dest.nil?
  Log.log.info("Downloading: #{file_url}")
  Rest.new(base_url: file_url).call(operation: 'GET', save_to: file_dest)
  Result::Status.new("Saved to: #{file_dest}")
end

#action_genkey(private_key_path:, private_key_length: OAuth::Jwt::DEFAULT_PRIV_KEY_LENGTH) ⇒ Object



409
410
411
412
# File 'lib/aspera/cli/plugins/config.rb', line 409

def action_genkey(private_key_path:, private_key_length: OAuth::Jwt::DEFAULT_PRIV_KEY_LENGTH, **)
  OAuth::Jwt.generate_rsa_private_key(path: private_key_path, length: private_key_length)
  Result::Status.new("Generated #{private_key_length} bit RSA key: #{private_key_path}")
end

#action_initdemoObject



495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
# File 'lib/aspera/cli/plugins/config.rb', line 495

def action_initdemo
  cp = presets.config_presets
  if cp.key?(DEMO_PRESET)
    Log.log.warn { "Demo server preset already present: #{DEMO_PRESET}" }
  else
    Log.log.info { "Creating Demo server preset: #{DEMO_PRESET}" }
    cp[DEMO_PRESET] = {
      'url'                                    => "ssh://#{DEMO_SERVER}.asperasoft.com:33001",
      'username'                               => ASPERA,
      'ssAP'.downcase.reverse + 'drow'.reverse => DEMO_SERVER + ASPERA # cspell:disable-line
    }
  end
  cp[PresetManager::Key::DEFAULTS] ||= {}
  if cp[PresetManager::Key::DEFAULTS].key?(SERVER_COMMAND)
    Log.log.warn { "Server default preset already set to: #{cp[PresetManager::Key::DEFAULTS][SERVER_COMMAND]}" }
    Log.log.warn { "Use #{DEMO_PRESET} for demo: -P#{DEMO_PRESET}" } unless
      DEMO_PRESET.eql?(cp[PresetManager::Key::DEFAULTS][SERVER_COMMAND])
  else
    cp[PresetManager::Key::DEFAULTS][SERVER_COMMAND] = DEMO_PRESET
    Log.log.info { "Setting server default preset to : #{DEMO_PRESET}" }
  end
  Result::Status.new('Done')
end

#action_options(plugin_name:) ⇒ Object



546
547
548
549
550
551
552
553
554
555
556
557
558
559
# File 'lib/aspera/cli/plugins/config.rb', line 546

def action_options(plugin_name:, **)
  # Instantiate the plugin so that it registers all its options in context.options
  Plugins::Factory.instance.create(plugin_name.to_sym, context: context)
  rows = context.options.declared_options.map do |sym, opt|
    row = {
      option:      "--#{sym.to_s.tr('_', '-')}",
      description: opt.description.to_s
    }
    row[:allowed]    = opt.values.join('|') if opt.values&.any?
    row[:deprecated] = opt.deprecation      if opt.deprecation
    row
  end
  Result::ObjectList.new(rows, fields: %w[option description allowed deprecated])
end

#action_plugins_create(name:, folder: nil) ⇒ Object



460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
# File 'lib/aspera/cli/plugins/config.rb', line 460

def action_plugins_create(name:, folder: nil, **)
  plugin_name = name.downcase
  destination_folder = folder || File.join(context.main_folder, ASPERA_PLUGINS_FOLDERNAME)
  plugin_file = File.join(destination_folder, "#{plugin_name}.rb")
  content = <<~END_OF_PLUGIN_CODE
    require 'aspera/cli/plugins/base'
    module Aspera
      module Cli
        module Plugins
          class #{plugin_name.snake_to_capital} < Base
            command :example, description: 'example command', action: :action_example
            def action_example
              Result::Status.new('You called plugin #{plugin_name}')
            end
          end
        end
      end
    end
  END_OF_PLUGIN_CODE
  File.write(plugin_file, content)
  Result::Status.new("Created #{plugin_file}")
end

#action_plugins_listObject



447
448
449
450
451
452
453
454
455
456
457
458
# File 'lib/aspera/cli/plugins/config.rb', line 447

def action_plugins_list
  result = Plugins::Factory.instance.plugin_list.map do |name|
    plugin_class = Plugins::Factory.instance.plugin_class(name)
    {
      plugin: name,
      detect: TerminalFormatter.tick(plugin_class.respond_to?(:detect)),
      wizard: TerminalFormatter.tick(plugin_class.method_defined?(:wizard)),
      path:   Plugins::Factory.instance.plugin_source(name)
    }
  end
  Result::ObjectList.new(result, fields: %w[plugin detect wizard path])
end

#action_remote_certificate_chain(remote_url:) ⇒ Object



414
415
416
417
418
# File 'lib/aspera/cli/plugins/config.rb', line 414

def action_remote_certificate_chain(remote_url:, **)
  remote_chain = Rest.remote_certificate_chain(remote_url, as_string: false)
  Aspera.assert(remote_chain&.first) { "No certificate found for #{remote_url}" }
  Result::Text.new(remote_chain.map(&:to_pem).join("\n"))
end

#action_remote_certificate_name(remote_url:) ⇒ Object



426
427
428
429
430
# File 'lib/aspera/cli/plugins/config.rb', line 426

def action_remote_certificate_name(remote_url:, **)
  remote_chain = Rest.remote_certificate_chain(remote_url, as_string: false)
  Aspera.assert(remote_chain&.first) { "No certificate found for #{remote_url}" }
  Result::Text.new(remote_chain.first.subject.to_a.find { |name, _, _| name == 'CN' }[1])
end

#action_remote_certificate_only(remote_url:) ⇒ Object



420
421
422
423
424
# File 'lib/aspera/cli/plugins/config.rb', line 420

def action_remote_certificate_only(remote_url:, **)
  remote_chain = Rest.remote_certificate_chain(remote_url, as_string: false)
  Aspera.assert(remote_chain&.first) { "No certificate found for #{remote_url}" }
  Result::Text.new(remote_chain.first.to_pem)
end

#action_test_throw(exception_class_name:, exception_text:) ⇒ Object

Raises:

  • (type)


561
562
563
564
565
# File 'lib/aspera/cli/plugins/config.rb', line 561

def action_test_throw(exception_class_name:, exception_text:, **)
  type = Object.const_get(exception_class_name)
  Aspera.assert(type <= Exception) { "#{type} is not an exception: #{type.class}" }
  raise type, exception_text
end

#action_tokens_show(token_id:) ⇒ Object



440
441
442
443
444
445
# File 'lib/aspera/cli/plugins/config.rb', line 440

def action_tokens_show(token_id:, **)
  require 'aspera/api/node'
  data = OAuth::Factory.instance.get_token_info(token_id)
  Aspera.assert(!data.nil?, type: Cli::Error) { 'Unknown identifier' }
  Result::SingleObject.new(data)
end

#action_wizard(url:, plugin_name: nil, preset_name: '') ⇒ Object



489
490
491
492
493
# File 'lib/aspera/cli/plugins/config.rb', line 489

def action_wizard(url:, plugin_name: nil, preset_name: '', **)
  options.ask_missing_mandatory = true
  apps = @wizard.identify_plugins_for_url(url: url, plugin_name: plugin_name).freeze
  @wizard.find(apps, preset_name: preset_name)
end

#option_plugin_folderObject



361
362
363
# File 'lib/aspera/cli/plugins/config.rb', line 361

def option_plugin_folder
  return Plugins::Factory.instance.lookup_folders
end

#option_plugin_folder=(value) ⇒ Object



355
356
357
358
359
# File 'lib/aspera/cli/plugins/config.rb', line 355

def option_plugin_folder=(value)
  value = [value] unless value.is_a?(Array)
  Aspera.assert_array_all(value, String) { 'plugin folder(s)' }
  value.each { |f| Plugins::Factory.instance.add_lookup_folder(f) }
end

#option_presetObject



365
# File 'lib/aspera/cli/plugins/config.rb', line 365

def option_preset; 'write-only option'; end

#option_preset=(value) ⇒ Object



367
368
369
370
371
372
373
374
375
376
# File 'lib/aspera/cli/plugins/config.rb', line 367

def option_preset=(value)
  case value
  when Hash
    options.add_option_preset(value, 'set')
  when String
    options.add_option_preset(presets.by_name(value), 'set_by_name')
  else
    raise BadArgument, 'Preset definition must be a String for preset name, or Hash for set of values'
  end
end