aspera-cli 4.26.1 → 4.26.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +19 -1
  4. data/CONTRIBUTING.md +2 -5
  5. data/bin/ascli +2 -2
  6. data/lib/aspera/agent/direct.rb +1 -1
  7. data/lib/aspera/agent/factory.rb +4 -0
  8. data/lib/aspera/agent/httpgw.rb +1 -1
  9. data/lib/aspera/agent/node.rb +2 -2
  10. data/lib/aspera/api/aoc.rb +43 -36
  11. data/lib/aspera/api/cos_node.rb +1 -1
  12. data/lib/aspera/api/faspex.rb +7 -6
  13. data/lib/aspera/api/node.rb +7 -7
  14. data/lib/aspera/ascmd.rb +4 -4
  15. data/lib/aspera/ascp/installation.rb +6 -2
  16. data/lib/aspera/cli/context.rb +58 -0
  17. data/lib/aspera/cli/extended_value.rb +3 -2
  18. data/lib/aspera/cli/formatter.rb +83 -184
  19. data/lib/aspera/cli/http.rb +167 -0
  20. data/lib/aspera/cli/manager.rb +3 -2
  21. data/lib/aspera/cli/plugins/alee.rb +2 -2
  22. data/lib/aspera/cli/plugins/aoc.rb +78 -67
  23. data/lib/aspera/cli/plugins/ats.rb +16 -16
  24. data/lib/aspera/cli/plugins/base.rb +18 -12
  25. data/lib/aspera/cli/plugins/config.rb +166 -438
  26. data/lib/aspera/cli/plugins/console.rb +7 -7
  27. data/lib/aspera/cli/plugins/cos.rb +2 -2
  28. data/lib/aspera/cli/plugins/factory.rb +3 -0
  29. data/lib/aspera/cli/plugins/faspex.rb +21 -21
  30. data/lib/aspera/cli/plugins/faspex5.rb +64 -44
  31. data/lib/aspera/cli/plugins/faspio.rb +1 -1
  32. data/lib/aspera/cli/plugins/httpgw.rb +2 -2
  33. data/lib/aspera/cli/plugins/node.rb +79 -79
  34. data/lib/aspera/cli/plugins/orchestrator.rb +21 -26
  35. data/lib/aspera/cli/plugins/preview.rb +9 -9
  36. data/lib/aspera/cli/plugins/server.rb +7 -7
  37. data/lib/aspera/cli/plugins/shares.rb +2 -2
  38. data/lib/aspera/cli/preset_manager.rb +235 -0
  39. data/lib/aspera/cli/result.rb +310 -0
  40. data/lib/aspera/cli/{main.rb → runner.rb} +14 -155
  41. data/lib/aspera/cli/sync_actions.rb +14 -10
  42. data/lib/aspera/cli/terminal_formatter.rb +65 -0
  43. data/lib/aspera/cli/transfer_progress.rb +6 -6
  44. data/lib/aspera/cli/version.rb +1 -1
  45. data/lib/aspera/cli/wizard.rb +5 -6
  46. data/lib/aspera/command_line_builder.rb +1 -1
  47. data/lib/aspera/data_repository.rb +4 -0
  48. data/lib/aspera/dot_container.rb +1 -1
  49. data/lib/aspera/environment.rb +22 -3
  50. data/lib/aspera/faspex_gw.rb +1 -1
  51. data/lib/aspera/json_rpc.rb +6 -5
  52. data/lib/aspera/keychain/base.rb +1 -1
  53. data/lib/aspera/keychain/encrypted_hash.rb +1 -1
  54. data/lib/aspera/keychain/factory.rb +1 -1
  55. data/lib/aspera/keychain/macos_security.rb +1 -1
  56. data/lib/aspera/log.rb +6 -2
  57. data/lib/aspera/nagios.rb +2 -2
  58. data/lib/aspera/oauth/base.rb +5 -5
  59. data/lib/aspera/oauth/boot.rb +43 -0
  60. data/lib/aspera/oauth/factory.rb +37 -13
  61. data/lib/aspera/oauth/web.rb +2 -2
  62. data/lib/aspera/oauth.rb +1 -0
  63. data/lib/aspera/persistency_action_once.rb +2 -2
  64. data/lib/aspera/preview/file_types.rb +4 -0
  65. data/lib/aspera/products/connect.rb +3 -0
  66. data/lib/aspera/products/transferd.rb +2 -2
  67. data/lib/aspera/proxy_auto_config.rb +3 -3
  68. data/lib/aspera/rest.rb +8 -4
  69. data/lib/aspera/rest_error_analyzer.rb +4 -0
  70. data/lib/aspera/rest_list.rb +10 -3
  71. data/lib/aspera/schema/IBM Aspera Faspex API-5.0-enhanced.yaml +14 -4
  72. data/lib/aspera/schema/IBM Aspera on Cloud API-0.2.6-enhanced.yaml +827 -88
  73. data/lib/aspera/schema/async_tables.yaml +361 -0
  74. data/lib/aspera/schema/reader.rb +1 -1
  75. data/lib/aspera/schema/registry.rb +13 -6
  76. data/lib/aspera/secret_hider.rb +4 -0
  77. data/lib/aspera/sync/database.rb +14 -10
  78. data/lib/aspera/sync/operations.rb +5 -5
  79. data/lib/aspera/temp_file_manager.rb +4 -0
  80. data/lib/aspera/transfer/parameters.rb +4 -4
  81. data/lib/aspera/transfer/resumer.rb +1 -1
  82. data/lib/aspera/transfer/spec.schema.yaml +17 -8
  83. data/lib/aspera/transfer/uri.rb +1 -1
  84. data.tar.gz.sig +0 -0
  85. metadata +21 -14
  86. metadata.gz.sig +0 -0
@@ -11,6 +11,8 @@ require 'aspera/cli/info'
11
11
  require 'aspera/cli/transfer_progress'
12
12
  require 'aspera/cli/wizard'
13
13
  require 'aspera/cli/sync_actions'
14
+ require 'aspera/cli/preset_manager'
15
+ require 'aspera/cli/http'
14
16
  require 'aspera/ascp/installation'
15
17
  require 'aspera/sync/operations'
16
18
  require 'aspera/products/transferd'
@@ -73,7 +75,7 @@ module Aspera
73
75
  # @return [String] Product config folder (~/.aspera/<name>)
74
76
  def default_app_main_folder(app_name:)
75
77
  Aspera.assert_type(app_name, String)
76
- Aspera.assert(!app_name.empty?)
78
+ Aspera.assert(!app_name.empty?, 'app_name must not be empty')
77
79
  return File.join(module_family_folder, app_name)
78
80
  end
79
81
  end
@@ -81,24 +83,12 @@ module Aspera
81
83
  def initialize(**_)
82
84
  # We need to defer parsing of options until we have the config file, so we can use @extend with @preset
83
85
  super
84
- @use_plugin_defaults = true
85
- @config_presets = {}
86
- @config_checksum_on_disk = nil
87
86
  @vault_instance = nil
88
87
  @pac_exec = nil
89
88
  @sdk_default_location = false
90
- @option_insecure = false
91
- @option_warn_insecure_cert = true
92
- @option_ignore_cert_host_port = []
93
- @option_http_options = {}
94
- @ssl_warned_urls = []
95
89
  @option_cache_tokens = true
96
90
  @main_folder = nil
97
91
  @option_config_file = nil
98
- # Store is used for ruby https (OpenSSL::X509::Store)
99
- @certificate_store = nil
100
- # Paths are used for ascp
101
- @certificate_paths = nil
102
92
  @progress_bar = nil
103
93
  # Option to set main folder
104
94
  options.declare(
@@ -116,8 +106,10 @@ module Aspera
116
106
  default: File.join(@main_folder, DEFAULT_CONFIG_FILENAME)
117
107
  )
118
108
  options.parse_options!
119
- # Read config file (set @config_presets)
120
- read_config_file
109
+ # Instantiate PresetManager (reads config file) and inject into context
110
+ context.presets = PresetManager.new(config_file: @option_config_file)
111
+ # Instantiate Http and inject into context
112
+ context.http_config = Http.new
121
113
  setup_extended_value_handlers
122
114
  # Vault options
123
115
  options.declare(:secret, 'Secret for access keys')
@@ -127,7 +119,7 @@ module Aspera
127
119
  # Declare generic plugin options only after handlers are declared
128
120
  Base.declare_options(options)
129
121
  # Configuration options
130
- options.declare(:no_default, 'Do not load default configuration for plugin', allowed: Allowed::TYPES_NONE, short: 'N'){@use_plugin_defaults = false}
122
+ options.declare(:no_default, 'Do not load default configuration for plugin', allowed: Allowed::TYPES_NONE, short: 'N'){ presets.use_plugin_defaults = false }
131
123
  options.declare(:preset, 'Load the named option preset from current config file', short: 'P', handler: {o: self, m: :option_preset})
132
124
  options.declare(:version_check_days, 'Period in days to check new version (zero to disable)', allowed: Allowed::TYPES_INTEGER, default: DEFAULT_CHECK_NEW_VERSION_DAYS)
133
125
  options.declare(:plugin_folder, 'Folder where to find additional plugins', handler: {o: self, m: :option_plugin_folder})
@@ -144,13 +136,8 @@ module Aspera
144
136
  options.declare(:smtp, 'Email: SMTP configuration', allowed: Hash)
145
137
  options.declare(:notify_to, 'Email: Recipient for notification of transfers')
146
138
  options.declare(:notify_template, 'Email: ERB template for notification of transfers')
147
- # HTTP options
148
- options.declare(:insecure, 'HTTP/S: Do not validate any certificate', allowed: Allowed::TYPES_BOOLEAN, handler: {o: self, m: :option_insecure}, default: false)
149
- options.declare(:ignore_certificate, 'HTTP/S: Do not validate certificate for these URLs', allowed: [Array, NilClass], handler: {o: self, m: :option_ignore_cert_host_port})
150
- options.declare(:warn_insecure, 'HTTP/S: Issue a warning if certificate is ignored', allowed: Allowed::TYPES_BOOLEAN, handler: {o: self, m: :option_warn_insecure_cert}, default: true)
151
- options.declare(:cert_stores, 'HTTP/S: List of folder with trusted certificates', allowed: Allowed::TYPES_STRING_ARRAY, handler: {o: self, m: :trusted_cert_locations})
152
- options.declare(:http_options, 'HTTP/S: Options for HTTP/S socket', allowed: Hash, handler: {o: self, m: :option_http_options}, default: {})
153
- options.declare(:http_proxy, 'HTTP/S: URL for proxy with optional credentials', handler: {o: self, m: :option_http_proxy})
139
+ # HTTP options — declared by HttpConfig itself
140
+ context.http_config.declare_options(options)
154
141
  options.declare(:cache_tokens, 'Save and reuse OAuth tokens', allowed: Allowed::TYPES_BOOLEAN, handler: {o: self, m: :option_cache_tokens})
155
142
  options.declare(:fpac, 'Proxy auto configuration script')
156
143
  options.declare(:proxy_credentials, 'HTTP proxy credentials for fpac: user, password', allowed: [Array, NilClass])
@@ -169,7 +156,7 @@ module Aspera
169
156
  end
170
157
 
171
158
  def setup_extended_value_handlers
172
- ExtendedValue.instance.on(EXTEND_PRESET){ |v| preset_by_name(v)}
159
+ ExtendedValue.instance.on(EXTEND_PRESET){ |v| presets.by_name(v)}
173
160
  ExtendedValue.instance.on(EXTEND_VAULT){ |v| vault_value(v)}
174
161
  ExtendedValue.instance.on(EXTEND_ARGS){ |v| options.args_as_extended(v)}
175
162
  add_plugin_default_preset(CONF_GLOBAL_SYM)
@@ -191,11 +178,12 @@ module Aspera
191
178
  def setup_rest_and_transfer_runtime
192
179
  RestParameters.instance.user_agent = Info::CMD_NAME
193
180
  RestParameters.instance.progress_bar = @progress_bar
194
- RestParameters.instance.session_cb = ->(http_session){update_http_session(http_session)}
181
+ RestParameters.instance.session_cb = ->(http_session){ context.http_config.update_session(http_session)}
195
182
  RestParameters.instance.spinner_cb = ->(title = nil, action: :spin){formatter.long_operation(title, action: action)}
196
- # Check http options that are global
183
+ # Promote http_options keys that target global singletons (RestParameters, SSL, OAuth)
184
+ http_opts = context.http_config.http_options
197
185
  keys_to_delete = []
198
- @option_http_options.each do |k, v|
186
+ http_opts.each do |k, v|
199
187
  method = "#{k}=".to_sym
200
188
  if RestParameters.instance.respond_to?(method)
201
189
  keys_to_delete.push(k)
@@ -208,7 +196,7 @@ module Aspera
208
196
  OAuth::Factory.instance.parameters[k.to_sym] = v
209
197
  end
210
198
  end
211
- keys_to_delete.each{ |k| @option_http_options.delete(k)}
199
+ keys_to_delete.each{ |k| http_opts.delete(k)}
212
200
  OAuth::Factory.instance.persist_mgr = persistency if @option_cache_tokens
213
201
  OAuth::Web.additional_info = "#{Info::CMD_NAME} v#{Cli::VERSION}"
214
202
  Transfer::Parameters.file_list_folder = File.join(@main_folder, FILE_LIST_FOLDER_NAME)
@@ -219,8 +207,20 @@ module Aspera
219
207
 
220
208
  public
221
209
 
222
- attr_accessor :main_folder, :option_cache_tokens, :option_insecure, :option_warn_insecure_cert, :option_http_options
223
- attr_reader :option_ignore_cert_host_port, :progress_bar
210
+ attr_accessor :main_folder, :option_cache_tokens
211
+ attr_reader :progress_bar
212
+
213
+ # Delegations to http_config kept for backward compatibility with transfer_agent and plugins
214
+ def option_insecure; context.http_config.insecure; end
215
+ def option_insecure=(v); context.http_config.insecure = v; end
216
+ def option_warn_insecure_cert; context.http_config.warn_insecure; end
217
+ def option_warn_insecure_cert=(v); context.http_config.warn_insecure = v; end
218
+ def option_http_options; context.http_config.http_options; end
219
+ def option_http_options=(v); context.http_config.http_options = v; end
220
+ def option_ignore_cert_host_port; context.http_config.ignore_cert_host_port; end
221
+ def ignore_cert?(address, port); context.http_config.ignore_cert?(address, port); end
222
+ def trusted_cert_locations; context.http_config.trusted_cert_locations; end
223
+ def trusted_cert_locations=(v); context.http_config.trusted_cert_locations = v; end
224
224
 
225
225
  def set_sdk_dir
226
226
  # Check SDK folder is set or not, for compatibility, we check in two places
@@ -243,114 +243,6 @@ module Aspera
243
243
  end
244
244
  end
245
245
 
246
- # Add files, folders or default locations to the certificate store
247
- # @param path_list [Array<String>] List of paths to add
248
- # @return the list of paths
249
- def trusted_cert_locations=(path_list)
250
- Aspera.assert_type(path_list, Array){'cert locations'}
251
- if @certificate_store.nil?
252
- Log.log.debug('Creating SSL Cert store')
253
- @certificate_store = OpenSSL::X509::Store.new
254
- @certificate_paths = []
255
- end
256
-
257
- path_list.each do |path|
258
- Aspera.assert_type(path, String){'Expecting a String for certificate location'}
259
- paths_to_add = [path]
260
- Log.log.debug{"Adding cert location: #{path}"}
261
- if path.eql?(SpecialValues::DEF)
262
- @certificate_store.set_default_paths
263
- paths_to_add = [OpenSSL::X509::DEFAULT_CERT_DIR]
264
- # JRuby cert file seems not to be PEM
265
- paths_to_add.push(OpenSSL::X509::DEFAULT_CERT_FILE) unless defined?(JRUBY_VERSION)
266
- paths_to_add.select!{ |f| File.exist?(f)}
267
- elsif File.file?(path)
268
- @certificate_store.add_file(path)
269
- elsif File.directory?(path)
270
- @certificate_store.add_path(path)
271
- else
272
- raise "No such file or folder: #{path}"
273
- end
274
- paths_to_add.each do |p|
275
- pp = [File.realpath(p)]
276
- if File.directory?(p)
277
- pp = Dir.entries(p)
278
- .map{ |e| File.realpath(File.join(p, e))}
279
- .select{ |entry| File.file?(entry)}
280
- .select{ |entry| CERT_EXT.any?{ |ext| entry.end_with?(ext)}}
281
- end
282
- @certificate_paths.concat(pp)
283
- end
284
- end
285
- @certificate_paths.uniq!
286
- end
287
-
288
- # @return only files
289
- def trusted_cert_locations
290
- locations = @certificate_paths
291
- if locations.nil?
292
- # Compute default locations
293
- self.trusted_cert_locations = [SpecialValues::DEF]
294
- locations = @certificate_paths
295
- # Restore defaults
296
- @certificate_paths = @certificate_store = nil
297
- end
298
- return locations
299
- end
300
-
301
- def option_http_proxy
302
- return ENV['http_proxy']
303
- end
304
-
305
- def option_http_proxy=(value)
306
- URI.parse(value)
307
- ENV['http_proxy'] = value
308
- end
309
-
310
- def option_ignore_cert_host_port=(url_list)
311
- url_list.each do |url|
312
- uri = URI.parse(url)
313
- raise "Expecting https scheme: #{url}" unless uri.scheme.eql?('https')
314
- @option_ignore_cert_host_port.push([uri.host, uri.port].freeze)
315
- end
316
- end
317
-
318
- def ignore_cert?(address, port)
319
- endpoint = [address, port].freeze
320
- ignore_cert = false
321
- if @option_insecure || @option_ignore_cert_host_port.any?(endpoint)
322
- ignore_cert = true
323
- if @option_warn_insecure_cert
324
- base_url = "https://#{address}:#{port}"
325
- if !@ssl_warned_urls.include?(base_url)
326
- Log.log.warn{"Ignoring certificate for: #{base_url}. Do not deactivate certificate verification in production."}
327
- @ssl_warned_urls.push(base_url)
328
- end
329
- end
330
- end
331
- Log.log.debug{"ignore cert? #{endpoint} -> #{ignore_cert}"}
332
- return ignore_cert
333
- end
334
-
335
- # Called every time a new REST HTTP session is opened to set user-provided options
336
- # @param http_session [Net::HTTP] the newly created HTTP/S session object
337
- def update_http_session(http_session)
338
- http_session.set_debug_output(LineLogger.new(:trace2)) if Log.instance.logger.trace2?
339
- # Rest.io_http_session(http_session).debug_output = Log.log
340
- http_session.verify_mode = SELF_SIGNED_CERT if http_session.use_ssl? && ignore_cert?(http_session.address, http_session.port)
341
- http_session.cert_store = @certificate_store if @certificate_store
342
- Log.log.debug{"Using cert store #{http_session.cert_store} (#{@certificate_store})"} unless http_session.cert_store.nil?
343
- @option_http_options.each do |k, v|
344
- method = "#{k}=".to_sym
345
- # Check if accessor is a method of Net::HTTP
346
- # continue_timeout= read_timeout= write_timeout=
347
- if http_session.respond_to?(method)
348
- http_session.send(method, v)
349
- else
350
- Log.log.error{"Unknown HTTP session attribute: #{k}"}
351
- end
352
- end
353
- end
354
246
 
355
247
  def check_gem_version
356
248
  latest_version =
@@ -401,89 +293,45 @@ module Aspera
401
293
  end if check_data[:need_update]
402
294
  end
403
295
 
404
- # Loads default parameters of plugin if no -P parameter
405
- # and if there is a section defined for the plugin in the "default" section
406
- # Try to find: conf[conf["default"][plugin_str]]
407
- # @param plugin_name_sym : symbol for plugin name
296
+ # Delegation to PresetManager: loads default preset options for a plugin
408
297
  def add_plugin_default_preset(plugin_name_sym)
409
- default_config_name = get_plugin_default_config_name(plugin_name_sym)
298
+ default_config_name = presets.plugin_default_name(plugin_name_sym)
410
299
  Log.log.debug{"add_plugin_default_preset:#{plugin_name_sym}:#{default_config_name}"}
411
- options.add_option_preset(preset_by_name(default_config_name), 'default_plugin', override: false) unless default_config_name.nil?
412
- return
413
- end
414
-
415
- # Get the default global preset, or set default one
416
- def global_default_preset
417
- result = get_plugin_default_config_name(CONF_GLOBAL_SYM)
418
- if result.nil?
419
- result = CONF_PRESET_GLOBAL
420
- set_preset_key(CONF_PRESET_DEFAULTS, CONF_GLOBAL_SYM, result)
421
- end
422
- return result
300
+ options.add_option_preset(presets.by_name(default_config_name), 'default_plugin', override: false) unless default_config_name.nil?
423
301
  end
424
302
 
303
+ # Delegations to PresetManager kept for backward compatibility
425
304
  def defaults_set(plugin_name, preset_name, preset_values, option_default, option_override)
426
- @config_presets[CONF_PRESET_DEFAULTS] ||= {}
427
- raise Cli::Error, "A default configuration already exists for plugin '#{plugin_name}' (use --override=yes or --default=no)" \
428
- if !option_override && option_default && @config_presets[CONF_PRESET_DEFAULTS].key?(plugin_name)
429
- raise Cli::Error, "Preset already exists: #{preset_name} (use --override=yes or provide alternate name on command line)" \
430
- if !option_override && @config_presets.key?(preset_name)
431
- if option_default
432
- Log.log.info("Setting config preset as default for #{plugin_name}")
433
- @config_presets[CONF_PRESET_DEFAULTS][plugin_name.to_s] = preset_name
434
- end
435
- @config_presets[preset_name] = preset_values
305
+ presets.defaults_set(plugin_name, preset_name, preset_values, option_default, option_override)
436
306
  end
437
307
 
438
308
  def set_preset_key(preset, param_name, param_value)
439
- Aspera.assert_type(param_name, String, Symbol){'parameter'}
440
- param_name = param_name.to_s
441
- selected_preset = @config_presets[preset]
442
- if selected_preset.nil?
443
- Log.log.debug{"Unknown preset name: #{preset}, initializing"}
444
- selected_preset = @config_presets[preset] = {}
445
- end
446
- Aspera.assert_type(selected_preset, Hash){"#{preset}.#{param_name}"}
447
- if selected_preset.key?(param_name)
448
- if selected_preset[param_name].eql?(param_value)
449
- Log.log.warn{"keeping same value for #{preset}: #{param_name}: #{param_value}"}
450
- return
451
- end
452
- Log.log.warn{"overwriting value for #{param_name}: #{selected_preset[param_name]}"}
453
- end
454
- selected_preset[param_name] = param_value
455
- Log.log.info("Updated: #{preset}: #{param_name} <- #{param_value}")
456
- nil
309
+ presets.set_key(preset, param_name, param_value)
457
310
  end
458
311
 
459
- # Set parameter and value in global config
460
- # Creates one if none already created
461
- # @return preset name that contains global default
462
312
  def set_global_default(key, value)
463
- set_preset_key(global_default_preset, key, value)
313
+ presets.set_global_default(key, value)
464
314
  end
465
315
 
466
- # $HOME/.aspera/`program_name`
467
- attr_reader :gem_url
468
- attr_accessor :option_config_file
469
-
470
- # @param config_name name of the preset in config file
471
- # @param include_path used to detect and avoid include loops
472
- # @return copy of the hash from name (also expands possible includes)
473
316
  def preset_by_name(config_name, include_path = [])
474
- raise Cli::Error, 'loop in include' if include_path.include?(config_name)
475
- include_path = include_path.clone # Avoid messing up if there are multiple branches
476
- current = @config_presets
477
- config_name.split(PRESET_DIG_SEPARATOR).each do |name|
478
- Aspera.assert_type(current, Hash, type: Cli::Error){"sub key: #{include_path}"}
479
- include_path.push(name)
480
- current = current[name]
481
- raise Cli::Error, "Unknown config preset: #{include_path}" if current.nil?
482
- end
483
- current = self.class.deep_clone(current) unless current.is_a?(String)
484
- return ExtendedValue.instance.evaluate(current, context: 'preset')
317
+ presets.by_name(config_name, include_path)
318
+ end
319
+
320
+ def get_plugin_default_config_name(plugin_name_sym)
321
+ presets.plugin_default_name(plugin_name_sym)
322
+ end
323
+
324
+ def save_config_file_if_needed
325
+ presets.save_if_needed
326
+ end
327
+
328
+ def lookup_preset(url:, username:)
329
+ presets.lookup_preset(url: url, username: username)
485
330
  end
486
331
 
332
+ attr_reader :gem_url
333
+ attr_accessor :option_config_file
334
+
487
335
  def option_plugin_folder=(value)
488
336
  value = [value] unless value.is_a?(Array)
489
337
  Aspera.assert_array_all(value, String){'plugin folder(s)'}
@@ -507,78 +355,19 @@ module Aspera
507
355
  end
508
356
  end
509
357
 
510
- # @return [Integer]
511
- def config_checksum
512
- Digest::SHA1.hexdigest(JSON.generate(@config_presets))
513
- end
514
-
515
- # Read config file and validate format
516
- def read_config_file
517
- Log.log.debug{"config file is: #{@option_config_file}".red}
518
- # Files search for configuration, by default the one given by user
519
- search_files = [@option_config_file]
520
- # Find first existing file (or nil)
521
- conf_file_to_load = search_files.find{ |f| File.exist?(f)}
522
- # If no file found, create default config
523
- if conf_file_to_load.nil?
524
- Log.log.warn{"No config file found. New configuration file: #{@option_config_file}"}
525
- @config_presets = {CONF_PRESET_CONFIG => {CONF_PRESET_VERSION => 'new file'}}
526
- # @config_checksum_on_disk is nil
527
- else
528
- Log.log.debug{"loading #{@option_config_file}"}
529
- @config_presets = YAML.load_file(conf_file_to_load)
530
- @config_checksum_on_disk = config_checksum
531
- end
532
- files_to_copy = []
533
- Log.dump(:available_presets, @config_presets, level: :trace1)
534
- Aspera.assert_type(@config_presets, Hash){'config file YAML'}
535
- # Check there is at least the config section
536
- Aspera.assert(@config_presets.key?(CONF_PRESET_CONFIG)){"Cannot find key: #{CONF_PRESET_CONFIG}"}
537
- version = @config_presets[CONF_PRESET_CONFIG][CONF_PRESET_VERSION]
538
- raise Error, 'No version found in config section.' if version.nil?
539
- Log.log.debug{"conf version: #{version}"}
540
- # VVV if there are any conversion needed, those happen here.
541
- # Fix bug in 4.4 (creating key "true" in "default" preset)
542
- @config_presets[CONF_PRESET_DEFAULTS].delete(true) if @config_presets[CONF_PRESET_DEFAULTS].is_a?(Hash)
543
- # ^^^ Place new compatibility code before this line
544
- # Set version to current
545
- @config_presets[CONF_PRESET_CONFIG][CONF_PRESET_VERSION] = Cli::VERSION
546
- unless files_to_copy.empty?
547
- Log.log.warn('Copying referenced files')
548
- files_to_copy.each do |file|
549
- FileUtils.cp(file, @main_folder)
550
- Log.log.warn{"#{file} -> #{@main_folder}"}
551
- end
552
- end
553
- return
554
- rescue Psych::SyntaxError => e
555
- Log.log.error('YAML error in config file')
556
- raise e
557
- rescue StandardError => e
558
- Log.log.debug{"-> #{e.class.name} : #{e}"}
559
- if File.exist?(@option_config_file)
560
- # Then there is a problem with that file.
561
- new_name = "#{@option_config_file}.pre#{Cli::VERSION}.manual_conversion_needed"
562
- File.rename(@option_config_file, new_name)
563
- Log.log.warn{"Renamed config file to #{new_name}."}
564
- Log.log.warn('Manual Conversion is required. Next time, a new empty file will be created.')
565
- end
566
- raise Cli::Error, e.to_s
567
- end
568
-
569
358
  def install_transfer_sdk
570
359
  asked_version = options.get_next_argument('transferd version', mandatory: false)
571
360
  sdk_url = options.get_option(:sdk_url, mandatory: true)
572
361
  sdk_url = nil if sdk_url.eql?(SpecialValues::DEF)
573
362
  name, version, folder = Ascp::Installation.instance.retrieve_sdk(url: sdk_url, version: asked_version)
574
- return Main.result_status("Installed #{name} version #{version} in #{folder}")
363
+ return Result::Status.new("Installed #{name} version #{version} in #{folder}")
575
364
  end
576
365
 
577
366
  def execute_action_ascp
578
367
  command = options.get_next_command(%i[show products info install spec schema errors])
579
368
  case command
580
369
  when :show
581
- return Main.result_text(Ascp::Installation.instance.path(:ascp))
370
+ return Result::Text.new(Ascp::Installation.instance.path(:ascp))
582
371
  when :info
583
372
  # Collect info from ascp executable
584
373
  data = Ascp::Installation.instance.ascp_info
@@ -588,30 +377,30 @@ module Aspera
588
377
  DataRepository::ELEMENTS.each_with_object(data){ |i, h| h[i.to_s] = DataRepository.instance.item(i)}
589
378
  # Declare those as secrets
590
379
  SecretHider::ADDITIONAL_KEYS_TO_HIDE.concat(DataRepository::ELEMENTS.map(&:to_s))
591
- return Main.result_single_object(data)
380
+ return Result::SingleObject.new(data)
592
381
  when :products
593
382
  command = options.get_next_command(%i[list])
594
383
  case command
595
384
  when :list
596
- return Main.result_object_list(Ascp::Installation.instance.installed_products, fields: %w[name app_root])
385
+ return Result::ObjectList.new(Ascp::Installation.instance.installed_products, fields: %w[name app_root])
597
386
  end
598
387
  when :install
599
388
  return install_transfer_sdk
600
389
  when :spec
601
390
  builder = Schema::Documentation.new(TerminalFormatter, Transfer::Spec::SCHEMA, include_option: true, agent_columns: true).build
602
- return Main.result_object_list(builder.rows, fields: builder.columns)
391
+ return Result::ObjectList.new(builder.rows, fields: builder.columns)
603
392
  when :schema
604
393
  schema = Transfer::Spec::SCHEMA.current.merge({'$comment'=>'DO NOT EDIT, this file was generated from the YAML.'})
605
394
  agent = options.get_next_argument('transfer agent name', mandatory: false)
606
395
  schema['properties'] = schema['properties'].select{ |_k, v| CommandLineBuilder.supported_by_agent(agent, v)} unless agent.nil?
607
396
  schema['properties'] = schema['properties'].sort.to_h
608
- return Main.result_single_object(schema)
397
+ return Result::SingleObject.new(schema)
609
398
  when :errors
610
399
  error_data = []
611
400
  Ascp::Management::ERRORS.each_pair do |code, prop|
612
401
  error_data.push(code: code, mnemonic: prop[:c], retry: prop[:r], info: prop[:a])
613
402
  end
614
- return Main.result_object_list(error_data)
403
+ return Result::ObjectList.new(error_data)
615
404
  else Aspera.error_unexpected_value(command)
616
405
  end
617
406
  Aspera.error_unreachable_line
@@ -624,7 +413,7 @@ module Aspera
624
413
  return install_transfer_sdk
625
414
  when :list
626
415
  sdk_list = Ascp::Installation.instance.sdk_locations
627
- return Main.result_object_list(
416
+ return Result::ObjectList.new(
628
417
  sdk_list,
629
418
  fields: sdk_list.first.keys - ['url']
630
419
  )
@@ -642,17 +431,18 @@ module Aspera
642
431
  PRESET_ALL_ACTIONS = (PRESET_GBL_ACTIONS + PRESET_INSTANCE_ACTIONS).freeze
643
432
 
644
433
  def execute_preset(action: nil, name: nil)
434
+ cp = presets.config_presets
645
435
  action = options.get_next_command(PRESET_ALL_ACTIONS) if action.nil?
646
436
  name = options.instance_identifier if name.nil? && PRESET_INSTANCE_ACTIONS.include?(action)
647
- name = global_default_preset if name.eql?(GLOBAL_DEFAULT_KEYWORD)
437
+ name = presets.global_default_preset if name.eql?(GLOBAL_DEFAULT_KEYWORD)
648
438
  # Those operations require existing option
649
- raise "no such preset: #{name}" if PRESET_EXIST_ACTIONS.include?(action) && !@config_presets.key?(name)
439
+ raise "no such preset: #{name}" if PRESET_EXIST_ACTIONS.include?(action) && !cp.key?(name)
650
440
  case action
651
441
  when :list
652
- return Main.result_value_list(@config_presets.keys, name: 'name')
442
+ return Result::ValueList.new(cp.keys, name: 'name')
653
443
  when :overview
654
444
  # Display process modifies the value (hide secrets): we do not want to save removed secrets
655
- data = self.class.deep_clone(@config_presets)
445
+ data = PresetManager.deep_clone(cp)
656
446
  formatter.hide_secrets(data)
657
447
  result = []
658
448
  data.each do |config, preset|
@@ -660,63 +450,62 @@ module Aspera
660
450
  result.push(CONF_OVERVIEW_KEYS.zip([config, parameter, value]).to_h)
661
451
  end
662
452
  end
663
- return Main.result_object_list(result, fields: CONF_OVERVIEW_KEYS)
453
+ return Result::ObjectList.new(result, fields: CONF_OVERVIEW_KEYS)
664
454
  when :show
665
- return Main.result_single_object(self.class.deep_clone(@config_presets[name]))
455
+ return Result::SingleObject.new(PresetManager.deep_clone(cp[name]))
666
456
  when :delete
667
- @config_presets.delete(name)
668
- return Main.result_status("Deleted: #{name}")
457
+ cp.delete(name)
458
+ return Result::Status.new("Deleted: #{name}")
669
459
  when :get
670
460
  param_name = options.get_next_argument('parameter name')
671
- value = @config_presets[name][param_name]
461
+ value = cp[name][param_name]
672
462
  raise "no such option in preset #{name} : #{param_name}" if value.nil?
673
463
  case value
674
- when Numeric, String then return Main.result_text(ExtendedValue.instance.evaluate(value.to_s, context: 'preset'))
464
+ when Numeric, String then return Result::Text.new(ExtendedValue.instance.evaluate(value.to_s, context: 'preset'))
675
465
  end
676
- return Main.result_single_object(value)
466
+ return Result::SingleObject.new(value)
677
467
  when :unset
678
468
  param_name = options.get_next_argument('parameter name')
679
- @config_presets[name].delete(param_name)
680
- return Main.result_status("Removed: #{name}: #{param_name}")
469
+ cp[name].delete(param_name)
470
+ return Result::Status.new("Removed: #{name}: #{param_name}")
681
471
  when :set
682
472
  param_name = options.get_next_argument('parameter name')
683
473
  param_name = Manager.option_line_to_name(param_name)
684
474
  param_value = options.get_next_argument('parameter value', validation: nil)
685
475
  set_preset_key(name, param_name, param_value)
686
- return Main.result_nothing
476
+ return Result::Nothing.new
687
477
  when :initialize
688
478
  config_value = options.get_next_argument('extended value', validation: Hash)
689
- Log.log.warn{"configuration already exists: #{name}, overwriting"} if @config_presets.key?(name)
690
- @config_presets[name] = config_value
691
- return Main.result_status("Modified: #{@option_config_file}")
479
+ Log.log.warn{"configuration already exists: #{name}, overwriting"} if cp.key?(name)
480
+ cp[name] = config_value
481
+ return Result::Status.new("Modified: #{@option_config_file}")
692
482
  when :update
693
- # get unprocessed options
694
483
  unprocessed_options = options.unprocessed_options_with_value
695
484
  Log.log.debug{"opts=#{unprocessed_options}"}
696
- @config_presets[name] ||= {}
697
- @config_presets[name].merge!(unprocessed_options)
698
- return Main.result_status("Updated: #{name}")
485
+ cp[name] ||= {}
486
+ cp[name].merge!(unprocessed_options)
487
+ return Result::Status.new("Updated: #{name}")
699
488
  when :ask
700
489
  options.ask_missing_mandatory = true
701
- @config_presets[name] ||= {}
490
+ cp[name] ||= {}
702
491
  options.get_next_argument('option names', multiple: true).each do |option_name|
703
492
  option_value = options.get_interactive(option_name, check_option: true)
704
- @config_presets[name][option_name] = option_value
493
+ cp[name][option_name] = option_value
705
494
  end
706
- return Main.result_status("Updated: #{name}")
495
+ return Result::Status.new("Updated: #{name}")
707
496
  when :lookup
708
497
  BasicAuth.declare_options(options)
709
498
  url = options.get_option(:url, mandatory: true)
710
499
  user = options.get_option(:username, mandatory: true)
711
500
  result = lookup_preset(url: url, username: user)
712
501
  raise Error, 'no such config found' if result.nil?
713
- return Main.result_single_object(result)
502
+ return Result::SingleObject.new(result)
714
503
  when :secure
715
504
  identifier = options.get_next_argument('config name', mandatory: false)
716
- preset_names = identifier.nil? ? @config_presets.keys : [identifier]
505
+ preset_names = identifier.nil? ? cp.keys : [identifier]
717
506
  secret_keywords = %w[password secret].freeze
718
507
  preset_names.each do |preset_name|
719
- preset = @config_presets[preset_name]
508
+ preset = cp[preset_name]
720
509
  next unless preset.is_a?(Hash)
721
510
  preset.each_key do |option_name|
722
511
  secret_keywords.each do |keyword|
@@ -729,13 +518,12 @@ module Aspera
729
518
  end
730
519
  to_set = {label: vault_label, password: preset[option_name]}
731
520
  puts "need to encode #{preset_name}.#{option_name} -> #{vault_label} -> #{to_set}"
732
- # to_copy=%i[]
733
521
  vault.set(to_set)
734
522
  preset[option_name] = "@vault:#{vault_label}.password"
735
523
  end
736
524
  end
737
525
  end
738
- return Main.result_status('Secrets secured in vault: Make sure to save the vault password securely.')
526
+ return Result::Status.new('Secrets secured in vault: Make sure to save the vault password securely.')
739
527
  end
740
528
  end
741
529
 
@@ -768,6 +556,7 @@ module Aspera
768
556
  vault
769
557
  test
770
558
  platform
559
+ completion
771
560
  ].freeze
772
561
 
773
562
  # Main action procedure for plugin
@@ -778,20 +567,20 @@ module Aspera
778
567
  return execute_preset
779
568
  when :open
780
569
  Environment.instance.open_editor(@option_config_file.to_s)
781
- return Main.result_nothing
570
+ return Result::Nothing.new
782
571
  when :documentation
783
572
  section = options.get_next_argument('private key file path', mandatory: false)
784
573
  section = "##{section}" unless section.nil?
785
574
  Environment.instance.open_uri("#{Info::DOC_URL}#{section}")
786
- return Main.result_nothing
575
+ return Result::Nothing.new
787
576
  when :genkey # Generate new rsa key
788
577
  private_key_path = options.get_next_argument('private key file path')
789
578
  private_key_length = options.get_next_argument('size in bits', mandatory: false, validation: Integer, default: OAuth::Jwt::DEFAULT_PRIV_KEY_LENGTH)
790
579
  OAuth::Jwt.generate_rsa_private_key(path: private_key_path, length: private_key_length)
791
- return Main.result_status("Generated #{private_key_length} bit RSA key: #{private_key_path}")
580
+ return Result::Status.new("Generated #{private_key_length} bit RSA key: #{private_key_path}")
792
581
  when :pubkey # Get pub key
793
582
  private_key_pem = options.get_next_argument('private key PEM value')
794
- return Main.result_text(OpenSSL::PKey::RSA.new(private_key_pem).public_key.to_s)
583
+ return Result::Text.new(OpenSSL::PKey::RSA.new(private_key_pem).public_key.to_s)
795
584
  when :remote_certificate
796
585
  cert_action = options.get_next_command(%i[chain only name])
797
586
  remote_url = options.get_next_argument('remote URL')
@@ -799,32 +588,32 @@ module Aspera
799
588
  raise "No certificate found for #{remote_url}" unless remote_chain&.first
800
589
  case cert_action
801
590
  when :chain
802
- return Main.result_text(remote_chain.map(&:to_pem).join("\n"))
591
+ return Result::Text.new(remote_chain.map(&:to_pem).join("\n"))
803
592
  when :only
804
- return Main.result_text(remote_chain.first.to_pem)
593
+ return Result::Text.new(remote_chain.first.to_pem)
805
594
  when :name
806
- return Main.result_text(remote_chain.first.subject.to_a.find{ |name, _, _| name == 'CN'}[1])
595
+ return Result::Text.new(remote_chain.first.subject.to_a.find{ |name, _, _| name == 'CN'}[1])
807
596
  end
808
597
  when :echo # Display the content of a value given on command line
809
- return Main.result_auto(options.get_next_argument('value', validation: nil))
598
+ return Result.auto(options.get_next_argument('value', validation: nil))
810
599
  when :download
811
600
  file_url = options.get_next_argument('source URL').chomp
812
601
  file_dest = options.get_next_argument('file path', mandatory: false)
813
602
  file_dest = File.join(transfer.destination_folder(Transfer::Spec::DIRECTION_RECEIVE), file_url.gsub(%r{.*/}, '')) if file_dest.nil?
814
603
  Log.log.info("Downloading: #{file_url}")
815
604
  Rest.new(base_url: file_url).call(operation: 'GET', save_to_file: file_dest)
816
- return Main.result_status("Saved to: #{file_dest}")
605
+ return Result::Status.new("Saved to: #{file_dest}")
817
606
  when :tokens
818
607
  require 'aspera/api/node'
819
608
  case options.get_next_command(%i{flush list show})
820
609
  when :flush
821
- return Main.result_value_list(OAuth::Factory.instance.flush_tokens, name: 'file')
610
+ return Result::ValueList.new(OAuth::Factory.instance.flush_tokens, name: 'file')
822
611
  when :list
823
- return Main.result_object_list(OAuth::Factory.instance.persisted_tokens)
612
+ return Result::ObjectList.new(OAuth::Factory.instance.persisted_tokens)
824
613
  when :show
825
614
  data = OAuth::Factory.instance.get_token_info(options.instance_identifier)
826
615
  raise Cli::Error, 'Unknown identifier' if data.nil?
827
- return Main.result_single_object(data)
616
+ return Result::SingleObject.new(data)
828
617
  end
829
618
  when :plugins
830
619
  case options.get_next_command(%i[list create])
@@ -839,7 +628,7 @@ module Aspera
839
628
  path: Plugins::Factory.instance.plugin_source(name)
840
629
  })
841
630
  end
842
- return Main.result_object_list(result, fields: %w[plugin detect wizard path])
631
+ return Result::ObjectList.new(result, fields: %w[plugin detect wizard path])
843
632
  when :create
844
633
  plugin_name = options.get_next_argument('name').downcase
845
634
  destination_folder = options.get_next_argument('folder', mandatory: false) || File.join(@main_folder, ASPERA_PLUGINS_FOLDERNAME)
@@ -852,7 +641,7 @@ module Aspera
852
641
  class #{plugin_name.snake_to_capital} < Base
853
642
  ACTIONS=[]
854
643
  def execute_action
855
- return Main.result_status('You called plugin #{plugin_name}')
644
+ return Result::Status.new('You called plugin #{plugin_name}')
856
645
  end
857
646
  end
858
647
  end
@@ -860,82 +649,102 @@ module Aspera
860
649
  end
861
650
  END_OF_PLUGIN_CODE
862
651
  File.write(plugin_file, content)
863
- return Main.result_status("Created #{plugin_file}")
652
+ return Result::Status.new("Created #{plugin_file}")
864
653
  end
865
654
  when :detect, :wizard
866
655
  # Interactive mode
867
656
  options.ask_missing_mandatory = true
868
657
  # Detect plugins by url and optional query
869
658
  apps = @wizard.identify_plugins_for_url.freeze
870
- return Main.result_object_list(apps) if action.eql?(:detect)
659
+ return Result::ObjectList.new(apps) if action.eql?(:detect)
871
660
  return @wizard.find(apps)
872
661
  when :coffee
873
- return Main.result_image(COFFEE_IMAGE_URL)
662
+ return Result::Image.new(COFFEE_IMAGE_URL)
874
663
  when :image
875
- return Main.result_image(options.get_next_argument('image URI or blob'))
664
+ return Result::Image.new(options.get_next_argument('image URI or blob'))
876
665
  when :ascp
877
666
  execute_action_ascp
878
667
  when :sync
668
+ SyncActions.declare_options(options)
879
669
  case options.get_next_command(%i[spec admin translate])
880
670
  when :spec
881
671
  builder = Schema::Documentation.new(TerminalFormatter, Sync::Operations::CONF_SCHEMA, include_option: true).build
882
- return Main.result_object_list(builder.rows, fields: builder.columns)
672
+ return Result::ObjectList.new(builder.rows, fields: builder.columns)
883
673
  when :admin
884
674
  return execute_sync_admin
885
675
  when :translate
886
- return Main.result_single_object(Sync::Operations.args_to_conf(options.get_next_argument('async arguments', multiple: true)))
676
+ return Result::SingleObject.new(Sync::Operations.args_to_conf(options.get_next_argument('async arguments', multiple: true)))
887
677
  else Aspera.error_unreachable_line
888
678
  end
889
679
  when :transferd
890
680
  execute_action_transferd
891
681
  when :gem
892
682
  case options.get_next_command(%i[path version name])
893
- when :path then return Main.result_text(self.class.gem_src_root)
894
- when :version then return Main.result_text(Cli::VERSION)
895
- when :name then return Main.result_text(Info::GEM_NAME)
683
+ when :path then return Result::Text.new(self.class.gem_src_root)
684
+ when :version then return Result::Text.new(Cli::VERSION)
685
+ when :name then return Result::Text.new(Info::GEM_NAME)
896
686
  else Aspera.error_unreachable_line
897
687
  end
898
688
  when :folder
899
- return Main.result_text(@main_folder)
689
+ return Result::Text.new(@main_folder)
900
690
  when :file
901
- return Main.result_text(@option_config_file)
691
+ return Result::Text.new(@option_config_file)
902
692
  when :email_test
903
693
  send_email_template(email_template_default: EMAIL_TEST_TEMPLATE)
904
- return Main.result_nothing
694
+ return Result::Nothing.new
905
695
  when :smtp_settings
906
- return Main.result_single_object(email_settings)
696
+ return Result::SingleObject.new(email_settings)
907
697
  when :proxy_check
908
698
  # Ensure fpac was provided
909
699
  options.get_option(:fpac, mandatory: true)
910
700
  server_url = options.get_next_argument('server url')
911
- return Main.result_text(@pac_exec.get_proxies(server_url))
701
+ return Result::ValueList.new(@pac_exec.get_proxies(server_url), name: 'proxy')
912
702
  when :check_update
913
- return Main.result_single_object(check_gem_version)
703
+ return Result::SingleObject.new(check_gem_version)
914
704
  when :initdemo
915
- if @config_presets.key?(DEMO_PRESET)
705
+ cp = presets.config_presets
706
+ if cp.key?(DEMO_PRESET)
916
707
  Log.log.warn{"Demo server preset already present: #{DEMO_PRESET}"}
917
708
  else
918
709
  Log.log.info{"Creating Demo server preset: #{DEMO_PRESET}"}
919
- @config_presets[DEMO_PRESET] = {
710
+ cp[DEMO_PRESET] = {
920
711
  'url' => "ssh://#{DEMO_SERVER}.asperasoft.com:33001",
921
712
  'username' => ASPERA,
922
713
  'ssAP'.downcase.reverse + 'drow'.reverse => DEMO_SERVER + ASPERA # cspell:disable-line
923
714
  }
924
715
  end
925
- @config_presets[CONF_PRESET_DEFAULTS] ||= {}
926
- if @config_presets[CONF_PRESET_DEFAULTS].key?(SERVER_COMMAND)
927
- Log.log.warn{"Server default preset already set to: #{@config_presets[CONF_PRESET_DEFAULTS][SERVER_COMMAND]}"}
716
+ cp[PresetManager::Key::DEFAULTS] ||= {}
717
+ if cp[PresetManager::Key::DEFAULTS].key?(SERVER_COMMAND)
718
+ Log.log.warn{"Server default preset already set to: #{cp[PresetManager::Key::DEFAULTS][SERVER_COMMAND]}"}
928
719
  Log.log.warn{"Use #{DEMO_PRESET} for demo: -P#{DEMO_PRESET}"} unless
929
- DEMO_PRESET.eql?(@config_presets[CONF_PRESET_DEFAULTS][SERVER_COMMAND])
720
+ DEMO_PRESET.eql?(cp[PresetManager::Key::DEFAULTS][SERVER_COMMAND])
930
721
  else
931
- @config_presets[CONF_PRESET_DEFAULTS][SERVER_COMMAND] = DEMO_PRESET
722
+ cp[PresetManager::Key::DEFAULTS][SERVER_COMMAND] = DEMO_PRESET
932
723
  Log.log.info{"Setting server default preset to : #{DEMO_PRESET}"}
933
724
  end
934
- return Main.result_status('Done')
725
+ return Result::Status.new('Done')
935
726
  when :vault then execute_vault
936
727
  when :test then return execute_test
937
728
  when :platform
938
- return Main.result_text(Environment.instance.architecture)
729
+ return Result::Text.new(Environment.instance.architecture)
730
+ when :completion
731
+ return execute_completion
732
+ else Aspera.error_unreachable_line
733
+ end
734
+ end
735
+
736
+ # Generate shell completion
737
+ # @return [Result] completion result
738
+ def execute_completion
739
+ shell_type = options.get_next_command(%i[bash])
740
+ case shell_type
741
+ when :bash
742
+ if options.get_next_argument('', multiple: true, mandatory: false).nil?
743
+ Plugins::Factory.instance.plugin_list.each{ |p| puts p}
744
+ else
745
+ Log.log.warn('only first level completion so far')
746
+ end
747
+ Process.exit(0)
939
748
  else Aspera.error_unreachable_line
940
749
  end
941
750
  end
@@ -1002,73 +811,28 @@ module Aspera
1002
811
  nil
1003
812
  end
1004
813
 
1005
- # Save current configuration to config file
1006
- # @return true if file was saved
1007
- def save_config_file_if_needed
1008
- raise Error, 'no configuration loaded' if @config_presets.nil?
1009
- current_checksum = config_checksum
1010
- return false if @config_checksum_on_disk.eql?(current_checksum)
1011
- FileUtils.mkdir_p(@main_folder)
1012
- Environment.restrict_file_access(@main_folder)
1013
- Log.log.info{"Saving config file: #{@option_config_file}"}
1014
- Environment.write_file_restricted(@option_config_file, force: true){@config_presets.to_yaml}
1015
- @config_checksum_on_disk = current_checksum
1016
- return true
1017
- end
1018
-
1019
- # @return [String] name if config_presets has default
1020
- # @return nil if there is no config or bypass default params
1021
- def get_plugin_default_config_name(plugin_name_sym)
1022
- Aspera.assert(!@config_presets.nil?){'config_presets shall be defined'}
1023
- if !@use_plugin_defaults
1024
- Log.log.debug('skip default config')
1025
- return
1026
- end
1027
- if !@config_presets.key?(CONF_PRESET_DEFAULTS)
1028
- Log.log.debug('No default section')
1029
- return
1030
- end
1031
- Aspera.assert_type(@config_presets[CONF_PRESET_DEFAULTS], Hash){'default section'}
1032
- if !@config_presets[CONF_PRESET_DEFAULTS].key?(plugin_name_sym.to_s)
1033
- Log.log.debug("No default for #{plugin_name_sym}")
1034
- return
1035
- end
1036
- default_config_name = @config_presets[CONF_PRESET_DEFAULTS][plugin_name_sym.to_s]
1037
- if !@config_presets.key?(default_config_name)
1038
- Log.log.error do
1039
- "Default config name [#{default_config_name}] specified for plugin [#{plugin_name_sym}], but it does not exist in config file.\n" \
1040
- "Please fix the issue: either create preset with one parameter:\n" \
1041
- "#{Info::CMD_NAME} config id #{default_config_name} init @json:'{}'\n" \
1042
- "or remove default:\n#{Info::CMD_NAME} config id default remove #{plugin_name_sym}"
1043
- end
1044
- raise Cli::Error, "No such preset: #{default_config_name}"
1045
- end
1046
- Aspera.assert_type(@config_presets[default_config_name], Hash, type: Cli::Error){'preset type'}
1047
- return default_config_name
1048
- end
1049
-
1050
814
  # @return [Hash] result of execution of vault command
1051
815
  def execute_vault
1052
816
  command = options.get_next_command(%i[info list show create delete password])
1053
817
  case command
1054
818
  when :info
1055
- return Main.result_single_object(vault.info)
819
+ return Result::SingleObject.new(vault.info)
1056
820
  when :list
1057
821
  # , fields: %w(label url username password description)
1058
- return Main.result_object_list(vault.list)
822
+ return Result::ObjectList.new(vault.list)
1059
823
  when :show
1060
- return Main.result_single_object(vault.get(label: options.get_next_argument('label')))
824
+ return Result::SingleObject.new(vault.get(label: options.get_next_argument('label')))
1061
825
  when :create
1062
826
  vault.set(options.get_next_argument('info', validation: Hash).symbolize_keys)
1063
- return Main.result_status('Secret added')
827
+ return Result::Status.new('Secret added')
1064
828
  when :delete
1065
829
  label_to_delete = options.get_next_argument('label')
1066
830
  vault.delete(label: label_to_delete)
1067
- return Main.result_status("Secret deleted: #{label_to_delete}")
831
+ return Result::Status.new("Secret deleted: #{label_to_delete}")
1068
832
  when :password
1069
- Aspera.assert(vault.respond_to?(:change_password)){'Vault does not support password change'}
833
+ Aspera.assert(vault.respond_to?(:change_password), 'Vault does not support password change')
1070
834
  vault.change_password(options.get_next_argument('new_password'))
1071
- return Main.result_status('Vault password updated')
835
+ return Result::Status.new('Vault password updated')
1072
836
  end
1073
837
  end
1074
838
 
@@ -1112,25 +876,6 @@ module Aspera
1112
876
  end
1113
877
  end
1114
878
 
1115
- # Version of URL without trailing "/" and removing default port
1116
- def canonical_url(url)
1117
- url.chomp('/').sub(%r{^(https://[^/]+):443$}, '\1')
1118
- end
1119
-
1120
- # Look for a preset that has the corresponding URL and username
1121
- # @return the first one matching
1122
- def lookup_preset(url:, username:)
1123
- # Remove extra info to maximize match
1124
- url = canonical_url(url)
1125
- Log.log.debug{"Lookup preset for #{username}@#{url}"}
1126
- @config_presets.each_value do |v|
1127
- next unless v.is_a?(Hash)
1128
- conf_url = v['url'].is_a?(String) ? canonical_url(v['url']) : nil
1129
- return self.class.deep_clone(v) if conf_url.eql?(url) && v['username'].eql?(username)
1130
- end
1131
- nil
1132
- end
1133
-
1134
879
  # Lookup the corresponding secret for the given URL and usernames
1135
880
  # @param url [String] Server URL
1136
881
  # @param username [String] Username
@@ -1138,7 +883,7 @@ module Aspera
1138
883
  def lookup_secret(url:, username:)
1139
884
  secret = options.get_option(:secret)
1140
885
  if secret.eql?('PRESET')
1141
- conf = lookup_preset(url: url, username: username)
886
+ conf = presets.lookup_preset(url: url, username: username)
1142
887
  if conf.is_a?(Hash)
1143
888
  Log.log.debug{"Found preset #{conf} with URL and username"}
1144
889
  secret = conf['password']
@@ -1146,17 +891,11 @@ module Aspera
1146
891
  end
1147
892
  return secret
1148
893
  end
1149
- # Private
1150
- # Folder in $HOME for application files (config, cache)
894
+ # Folder in $HOME for application files (~/.aspera)
1151
895
  ASPERA_HOME_FOLDER_NAME = '.aspera'
1152
- # Default config file
896
+ # Default config file name
1153
897
  DEFAULT_CONFIG_FILENAME = 'config.yaml'
1154
- # Reserved preset names
1155
- CONF_PRESET_CONFIG = 'config'
1156
- CONF_PRESET_VERSION = 'version'
1157
- CONF_PRESET_DEFAULTS = 'default'
1158
- CONF_PRESET_GLOBAL = 'global_common_defaults'
1159
- # Special name to identify value of default
898
+ # Used in execute_preset / execute_action to identify the global default keyword
1160
899
  GLOBAL_DEFAULT_KEYWORD = 'GLOBAL'
1161
900
  CONF_GLOBAL_SYM = :config
1162
901
  # Folder containing custom plugins in user's config folder
@@ -1180,43 +919,32 @@ module Aspera
1180
919
  EXTEND_PRESET = :preset
1181
920
  EXTEND_VAULT = :vault
1182
921
  EXTEND_ARGS = :''
1183
- PRESET_DIG_SEPARATOR = '.'
1184
922
  DEFAULT_CHECK_NEW_VERSION_DAYS = 7
1185
923
  COFFEE_IMAGE_URL = 'https://enjoyjava.com/wp-content/uploads/2018/01/How-to-make-strong-coffee.jpg'
1186
924
  GEM_CHECK_DATE_FMT = '%Y/%m/%d'
1187
- # For testing only
1188
- SELF_SIGNED_CERT = OpenSSL::SSL.const_get(:enon_yfirev.to_s.upcase.reverse) # cspell: disable-line
1189
925
  CONF_OVERVIEW_KEYS = %w[preset parameter value].freeze
1190
926
  SMTP_CONF_PARAMS = %i[server tls ssl port domain username password from_name from_email].freeze
1191
- CERT_EXT = %w[crt cer pem der].freeze
1192
927
  private_constant :ASPERA_HOME_FOLDER_NAME,
1193
928
  :DEFAULT_CONFIG_FILENAME,
1194
- :CONF_PRESET_CONFIG,
1195
- :CONF_PRESET_VERSION,
1196
- :CONF_PRESET_DEFAULTS,
1197
- :CONF_PRESET_GLOBAL,
929
+ :GLOBAL_DEFAULT_KEYWORD,
930
+ :CONF_GLOBAL_SYM,
1198
931
  :ASPERA_PLUGINS_FOLDERNAME,
932
+ :PERSISTENCY_FOLDER,
1199
933
  :FILE_LIST_FOLDER_NAME,
1200
934
  :REST_EXCEPTIONS_LOG_FILENAME,
1201
935
  :ASPERA,
936
+ :SERVER_COMMAND,
937
+ :TRANSFERD_APP_NAME,
1202
938
  :DEMO_SERVER,
1203
939
  :DEMO_PRESET,
1204
940
  :EMAIL_TEST_TEMPLATE,
1205
941
  :EXTEND_PRESET,
1206
942
  :EXTEND_VAULT,
1207
943
  :DEFAULT_CHECK_NEW_VERSION_DAYS,
1208
- :SERVER_COMMAND,
1209
- :PRESET_DIG_SEPARATOR,
1210
944
  :COFFEE_IMAGE_URL,
1211
- :SELF_SIGNED_CERT,
1212
- :PERSISTENCY_FOLDER,
1213
- :CONF_OVERVIEW_KEYS,
1214
- :SMTP_CONF_PARAMS,
1215
- :TRANSFERD_APP_NAME,
1216
- :GLOBAL_DEFAULT_KEYWORD,
1217
- :CONF_GLOBAL_SYM,
1218
945
  :GEM_CHECK_DATE_FMT,
1219
- :CERT_EXT
946
+ :CONF_OVERVIEW_KEYS,
947
+ :SMTP_CONF_PARAMS
1220
948
  end
1221
949
  end
1222
950
  end