aspera-cli 4.24.0 → 4.24.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 (87) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +19 -1
  4. data/README.md +1264 -941
  5. data/bin/ascli +20 -1
  6. data/bin/asession +23 -27
  7. data/lib/aspera/agent/base.rb +10 -21
  8. data/lib/aspera/agent/connect.rb +2 -3
  9. data/lib/aspera/agent/desktop.rb +2 -2
  10. data/lib/aspera/agent/direct.rb +49 -32
  11. data/lib/aspera/agent/factory.rb +31 -0
  12. data/lib/aspera/api/aoc.rb +79 -49
  13. data/lib/aspera/api/faspex.rb +212 -0
  14. data/lib/aspera/api/node.rb +99 -84
  15. data/lib/aspera/ascp/installation.rb +22 -21
  16. data/lib/aspera/ascp/management.rb +119 -23
  17. data/lib/aspera/assert.rb +14 -8
  18. data/lib/aspera/cli/extended_value.rb +15 -15
  19. data/lib/aspera/cli/formatter.rb +7 -5
  20. data/lib/aspera/cli/hints.rb +8 -0
  21. data/lib/aspera/cli/info.rb +4 -4
  22. data/lib/aspera/cli/main.rb +56 -71
  23. data/lib/aspera/cli/manager.rb +7 -4
  24. data/lib/aspera/cli/plugins/alee.rb +2 -1
  25. data/lib/aspera/cli/plugins/aoc.rb +110 -186
  26. data/lib/aspera/cli/plugins/ats.rb +4 -4
  27. data/lib/aspera/cli/plugins/base.rb +335 -0
  28. data/lib/aspera/cli/plugins/basic_auth.rb +45 -0
  29. data/lib/aspera/cli/plugins/config.rb +263 -221
  30. data/lib/aspera/cli/plugins/console.rb +15 -15
  31. data/lib/aspera/cli/plugins/cos.rb +2 -2
  32. data/lib/aspera/cli/plugins/factory.rb +78 -0
  33. data/lib/aspera/cli/plugins/faspex.rb +17 -20
  34. data/lib/aspera/cli/plugins/faspex5.rb +79 -193
  35. data/lib/aspera/cli/plugins/faspio.rb +14 -13
  36. data/lib/aspera/cli/plugins/httpgw.rb +13 -12
  37. data/lib/aspera/cli/plugins/node.rb +34 -32
  38. data/lib/aspera/cli/plugins/oauth.rb +48 -0
  39. data/lib/aspera/cli/plugins/orchestrator.rb +15 -13
  40. data/lib/aspera/cli/plugins/preview.rb +4 -4
  41. data/lib/aspera/cli/plugins/server.rb +15 -13
  42. data/lib/aspera/cli/plugins/shares.rb +18 -15
  43. data/lib/aspera/cli/sync_actions.rb +1 -1
  44. data/lib/aspera/cli/transfer_agent.rb +24 -20
  45. data/lib/aspera/cli/transfer_progress.rb +6 -6
  46. data/lib/aspera/cli/version.rb +3 -3
  47. data/lib/aspera/cli/wizard.rb +74 -65
  48. data/lib/aspera/colors.rb +6 -0
  49. data/lib/aspera/command_line_builder.rb +45 -50
  50. data/lib/aspera/command_line_converter.rb +2 -1
  51. data/lib/aspera/coverage.rb +1 -1
  52. data/lib/aspera/data_repository.rb +1 -1
  53. data/lib/aspera/environment.rb +13 -9
  54. data/lib/aspera/faspex_gw.rb +6 -4
  55. data/lib/aspera/faspex_postproc.rb +1 -1
  56. data/lib/aspera/keychain/macos_security.rb +1 -1
  57. data/lib/aspera/log.rb +88 -37
  58. data/lib/aspera/nagios.rb +1 -1
  59. data/lib/aspera/oauth/base.rb +17 -10
  60. data/lib/aspera/oauth/factory.rb +8 -8
  61. data/lib/aspera/oauth/web.rb +2 -2
  62. data/lib/aspera/products/connect.rb +4 -3
  63. data/lib/aspera/products/desktop.rb +1 -4
  64. data/lib/aspera/products/other.rb +9 -1
  65. data/lib/aspera/products/transferd.rb +0 -1
  66. data/lib/aspera/rest.rb +126 -83
  67. data/lib/aspera/ssh.rb +3 -3
  68. data/lib/aspera/sync/args.schema.yaml +46 -3
  69. data/lib/aspera/sync/conf.schema.yaml +130 -94
  70. data/lib/aspera/sync/operations.rb +71 -74
  71. data/lib/aspera/temp_file_manager.rb +17 -5
  72. data/lib/aspera/transfer/error.rb +16 -7
  73. data/lib/aspera/transfer/parameters.rb +34 -20
  74. data/lib/aspera/transfer/resumer.rb +74 -0
  75. data/lib/aspera/transfer/spec.rb +4 -3
  76. data/lib/aspera/transfer/spec.schema.yaml +132 -51
  77. data/lib/aspera/transfer/spec_doc.rb +41 -35
  78. data/lib/aspera/uri_reader.rb +1 -1
  79. data/lib/aspera/web_auth.rb +6 -6
  80. data.tar.gz.sig +0 -0
  81. metadata +9 -7
  82. metadata.gz.sig +2 -2
  83. data/lib/aspera/cli/basic_auth_plugin.rb +0 -43
  84. data/lib/aspera/cli/plugin.rb +0 -333
  85. data/lib/aspera/cli/plugin_factory.rb +0 -81
  86. data/lib/aspera/resumer.rb +0 -77
  87. data/lib/aspera/transfer/error_info.rb +0 -91
@@ -1,7 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # cspell:ignore initdemo genkey pubkey asperasoft filelists
4
- require 'aspera/cli/basic_auth_plugin'
4
+ require 'aspera/cli/plugins/basic_auth'
5
+ require 'aspera/cli/plugins/factory'
5
6
  require 'aspera/cli/extended_value'
6
7
  require 'aspera/cli/special_values'
7
8
  require 'aspera/cli/version'
@@ -10,8 +11,8 @@ require 'aspera/cli/info'
10
11
  require 'aspera/cli/transfer_progress'
11
12
  require 'aspera/cli/wizard'
12
13
  require 'aspera/ascp/installation'
14
+ require 'aspera/sync/operations'
13
15
  require 'aspera/products/transferd'
14
- require 'aspera/transfer/error_info'
15
16
  require 'aspera/transfer/parameters'
16
17
  require 'aspera/transfer/spec'
17
18
  require 'aspera/transfer/spec_doc'
@@ -36,93 +37,34 @@ require 'erb'
36
37
  module Aspera
37
38
  module Cli
38
39
  module Plugins
39
- # manage the CLI config file
40
- class Config < Cli::Plugin
41
- # folder in $HOME for application files (config, cache)
42
- ASPERA_HOME_FOLDER_NAME = '.aspera'
43
- # default config file
44
- DEFAULT_CONFIG_FILENAME = 'config.yaml'
45
- # reserved preset names
46
- CONF_PRESET_CONFIG = 'config'
47
- CONF_PRESET_VERSION = 'version'
48
- CONF_PRESET_DEFAULTS = 'default'
49
- CONF_PRESET_GLOBAL = 'global_common_defaults'
50
- # special name to identify value of default
51
- GLOBAL_DEFAULT_KEYWORD = 'GLOBAL'
52
- CONF_GLOBAL_SYM = :config
53
- # folder containing custom plugins in user's config folder
54
- ASPERA_PLUGINS_FOLDERNAME = 'plugins'
55
- PERSISTENCY_FOLDER = 'persist_store'
56
- ASPERA = 'aspera'
57
- SERVER_COMMAND = 'server'
58
- DIR_SDK = 'sdk'
59
- DEMO_SERVER = 'demo'
60
- DEMO_PRESET = 'demoserver' # cspell: disable-line
61
- EMAIL_TEST_TEMPLATE = <<~END_OF_TEMPLATE
62
- From: <%=from_name%> <<%=from_email%>>
63
- To: <<%=to%>>
64
- Subject: #{Info::GEM_NAME} email test
65
-
66
- This email was sent to test #{Info::CMD_NAME}.
67
- END_OF_TEMPLATE
68
- # special extended values
69
- EXTEND_PRESET = :preset
70
- EXTEND_VAULT = :vault
71
- PRESET_DIG_SEPARATOR = '.'
72
- DEFAULT_CHECK_NEW_VERSION_DAYS = 7
73
- COFFEE_IMAGE_URL = 'https://enjoyjava.com/wp-content/uploads/2018/01/How-to-make-strong-coffee.jpg'
74
- GEM_CHECK_DATE_FMT = '%Y/%m/%d'
75
- # for testing only
76
- SELF_SIGNED_CERT = OpenSSL::SSL.const_get(:enon_yfirev.to_s.upcase.reverse) # cspell: disable-line
77
- CONF_OVERVIEW_KEYS = %w[preset parameter value].freeze
78
- SMTP_CONF_PARAMS = %i[server tls ssl port domain username password from_name from_email].freeze
79
- private_constant :DEFAULT_CONFIG_FILENAME,
80
- :CONF_PRESET_CONFIG,
81
- :CONF_PRESET_VERSION,
82
- :CONF_PRESET_DEFAULTS,
83
- :CONF_PRESET_GLOBAL,
84
- :ASPERA_PLUGINS_FOLDERNAME,
85
- :ASPERA,
86
- :DEMO_SERVER,
87
- :DEMO_PRESET,
88
- :EMAIL_TEST_TEMPLATE,
89
- :EXTEND_PRESET,
90
- :EXTEND_VAULT,
91
- :DEFAULT_CHECK_NEW_VERSION_DAYS,
92
- :SERVER_COMMAND,
93
- :PRESET_DIG_SEPARATOR,
94
- :COFFEE_IMAGE_URL,
95
- :SELF_SIGNED_CERT,
96
- :PERSISTENCY_FOLDER,
97
- :CONF_OVERVIEW_KEYS,
98
- :SMTP_CONF_PARAMS
99
-
40
+ # Manage the CLI config file
41
+ class Config < Base
100
42
  class << self
101
- # folder containing plugins in the gem's main folder
43
+ # Folder containing plugins in the gem's main folder
102
44
  def gem_plugins_folder
103
45
  File.dirname(File.expand_path(__FILE__))
104
46
  end
105
47
 
106
48
  # @return main folder where code is, i.e. .../lib
107
- # go up as many times as englobing modules (not counting class, as it is a file)
49
+ # Go up as many times as englobing modules (not counting class, as it is a file)
108
50
  def gem_src_root
109
51
  # Module.nesting[2] is Cli::Plugins
110
52
  File.expand_path(Module.nesting[2].to_s.gsub('::', '/').gsub(%r{[^/]+}, '..'), gem_plugins_folder)
111
53
  end
112
54
 
113
- # deep clone hash so that it does not get modified in case of display and secret hide
55
+ # Deep clone hash so that it does not get modified in case of display and secret hide
114
56
  def deep_clone(val)
115
57
  return Marshal.load(Marshal.dump(val))
116
58
  end
117
59
 
118
- # return product family folder (~/.aspera)
60
+ # @return product family folder (~/.aspera)
119
61
  def module_family_folder
120
62
  user_home_folder = Dir.home
121
63
  Aspera.assert(Dir.exist?(user_home_folder), type: Cli::Error){"Home folder does not exist: #{user_home_folder}. Check your user environment."}
122
64
  return File.join(user_home_folder, ASPERA_HOME_FOLDER_NAME)
123
65
  end
124
66
 
125
- # return product config folder (~/.aspera/<name>)
67
+ # @return [String] Product config folder (~/.aspera/<name>)
126
68
  def default_app_main_folder(app_name:)
127
69
  Aspera.assert_type(app_name, String)
128
70
  Aspera.assert(!app_name.empty?)
@@ -131,7 +73,7 @@ module Aspera
131
73
  end
132
74
 
133
75
  def initialize(**_)
134
- # we need to defer parsing of options until we have the config file, so we can use @extend with @preset
76
+ # We need to defer parsing of options until we have the config file, so we can use @extend with @preset
135
77
  super
136
78
  @use_plugin_defaults = true
137
79
  @config_presets = nil
@@ -147,12 +89,12 @@ module Aspera
147
89
  @option_cache_tokens = true
148
90
  @main_folder = nil
149
91
  @option_config_file = nil
150
- # store is used for ruby https
92
+ # Store is used for ruby https
151
93
  @certificate_store = nil
152
- # paths are used for ascp
94
+ # Paths are used for ascp
153
95
  @certificate_paths = nil
154
96
  @progress_bar = nil
155
- # option to set main folder
97
+ # Option to set main folder
156
98
  options.declare(
157
99
  :home, 'Home folder for tool',
158
100
  handler: {o: self, m: :main_folder},
@@ -161,38 +103,38 @@ module Aspera
161
103
  )
162
104
  options.parse_options!
163
105
  Log.log.debug{"#{Info::CMD_NAME} folder: #{@main_folder}"}
164
- # data persistency manager, created by config plugin, set for global object
106
+ # Data persistency manager, created by config plugin, set for global object
165
107
  context.persistency = PersistencyFolder.new(File.join(@main_folder, PERSISTENCY_FOLDER))
166
- # set folders for plugin lookup
167
- PluginFactory.instance.add_lookup_folder(self.class.gem_plugins_folder)
168
- PluginFactory.instance.add_lookup_folder(File.join(@main_folder, ASPERA_PLUGINS_FOLDERNAME))
169
- # option to set config file
108
+ # Set folders for plugin lookup
109
+ Plugins::Factory.instance.add_lookup_folder(self.class.gem_plugins_folder)
110
+ Plugins::Factory.instance.add_lookup_folder(File.join(@main_folder, ASPERA_PLUGINS_FOLDERNAME))
111
+ # Option to set config file
170
112
  options.declare(
171
113
  :config_file, 'Path to YAML file with preset configuration',
172
114
  handler: {o: self, m: :option_config_file},
173
115
  default: File.join(@main_folder, DEFAULT_CONFIG_FILENAME)
174
116
  )
175
117
  options.parse_options!
176
- # read config file (set @config_presets)
118
+ # Read config file (set @config_presets)
177
119
  read_config_file
178
- # add preset handler (needed for smtp)
120
+ # Add preset handler (needed for smtp)
179
121
  ExtendedValue.instance.set_handler(EXTEND_PRESET, lambda{ |v| preset_by_name(v)})
180
122
  ExtendedValue.instance.set_handler(EXTEND_VAULT, lambda{ |v| vault_value(v)})
181
- # load defaults before it can be overridden
123
+ # Load defaults before it can be overridden
182
124
  add_plugin_default_preset(CONF_GLOBAL_SYM)
183
- # vault options
125
+ # Vault options
184
126
  options.declare(:secret, 'Secret for access keys')
185
127
  options.declare(:vault, 'Vault for secrets', types: Hash, default: {})
186
128
  options.declare(:vault_password, 'Vault password')
187
129
  options.parse_options!
188
- # declare generic plugin options only after handlers are declared
189
- Plugin.declare_generic_options(options)
190
- # configuration options
130
+ # Declare generic plugin options only after handlers are declared
131
+ Base.declare_options(options)
132
+ # Configuration options
191
133
  options.declare(:no_default, 'Do not load default configuration for plugin', values: :none, short: 'N'){@use_plugin_defaults = false}
192
134
  options.declare(:preset, 'Load the named option preset from current config file', short: 'P', handler: {o: self, m: :option_preset})
193
135
  options.declare(:version_check_days, 'Period in days to check new version (zero to disable)', coerce: Integer, default: DEFAULT_CHECK_NEW_VERSION_DAYS)
194
136
  options.declare(:plugin_folder, 'Folder where to find additional plugins', handler: {o: self, m: :option_plugin_folder})
195
- # declare wizard options
137
+ # Declare wizard options
196
138
  @wizard = Wizard.new(self, @main_folder)
197
139
  # Transfer SDK options
198
140
  options.declare(:ascp_path, 'Ascp: Path to ascp', handler: {o: Ascp::Installation.instance, m: :ascp_path})
@@ -201,7 +143,7 @@ module Aspera
201
143
  options.declare(:locations_url, 'Ascp: URL to get locations of Aspera Transfer Daemon', handler: {o: Ascp::Installation.instance, m: :transferd_urls})
202
144
  options.declare(:sdk_folder, 'Ascp: SDK folder path', handler: {o: Products::Transferd, m: :sdk_directory})
203
145
  options.declare(:progress_bar, 'Display progress bar', values: :bool, default: Environment.terminal?)
204
- # email options
146
+ # Email options
205
147
  options.declare(:smtp, 'Email: SMTP configuration', types: Hash)
206
148
  options.declare(:notify_to, 'Email: Recipient for notification of transfers')
207
149
  options.declare(:notify_template, 'Email: ERB template for notification of transfers')
@@ -222,21 +164,21 @@ module Aspera
222
164
  if sdk_dir.nil?
223
165
  @sdk_default_location = true
224
166
  Log.log.debug('SDK folder is not set, checking default')
225
- # new location
226
- sdk_dir = self.class.default_app_main_folder(app_name: DIR_SDK)
227
- Log.log.debug{"checking: #{sdk_dir}"}
167
+ # New location
168
+ sdk_dir = self.class.default_app_main_folder(app_name: TRANSFERD_APP_NAME)
169
+ Log.log.debug{"Checking: #{sdk_dir}"}
228
170
  if !Dir.exist?(sdk_dir)
229
- Log.log.debug{"not exists: #{sdk_dir}"}
230
- # former location
231
- former_sdk_folder = File.join(self.class.default_app_main_folder(app_name: Info::CMD_NAME), DIR_SDK)
232
- Log.log.debug{"checking: #{former_sdk_folder}"}
171
+ Log.log.debug{"No such folder: #{sdk_dir}"}
172
+ # Former location
173
+ former_sdk_folder = File.join(self.class.default_app_main_folder(app_name: Info::CMD_NAME), TRANSFERD_APP_NAME)
174
+ Log.log.debug{"Checking: #{former_sdk_folder}"}
233
175
  sdk_dir = former_sdk_folder if Dir.exist?(former_sdk_folder)
234
176
  end
235
- Log.log.debug{"using: #{sdk_dir}"}
177
+ Log.log.debug{"Using: #{sdk_dir}"}
236
178
  Products::Transferd.sdk_directory = sdk_dir
237
179
  end
238
180
  pac_script = options.get_option(:fpac)
239
- # create PAC executor
181
+ # Create PAC executor
240
182
  if !pac_script.nil?
241
183
  @pac_exec = ProxyAutoConfig.new(pac_script).register_uri_generic
242
184
  proxy_user_pass = options.get_option(:proxy_credentials)
@@ -249,23 +191,54 @@ module Aspera
249
191
  RestParameters.instance.user_agent = Info::CMD_NAME
250
192
  RestParameters.instance.progress_bar = @progress_bar
251
193
  RestParameters.instance.session_cb = lambda{ |http_session| update_http_session(http_session)}
252
- @option_http_options.keys.select{ |i| RestParameters.instance.respond_to?(i)}.each do |k|
194
+ # Check http options that are global
195
+ keys_to_delete = []
196
+ @option_http_options.each do |k, v|
253
197
  method = "#{k}=".to_sym
254
- RestParameters.instance.send(method, @option_http_options[k])
255
- @option_http_options.delete(k)
198
+ if RestParameters.instance.respond_to?(method)
199
+ keys_to_delete.push(k)
200
+ RestParameters.instance.send(method, v)
201
+ elsif k.eql?('ssl_options')
202
+ keys_to_delete.push(k)
203
+ # NOTE: here is a hack that allows setting SSLContext options
204
+ Aspera.assert_type(v, Array){'ssl_options'}
205
+ # Start with default options
206
+ ssl_options = OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:options]
207
+ v.each do |opt|
208
+ case opt
209
+ when Integer
210
+ ssl_options = opt
211
+ when String
212
+ name = "OP_#{opt.start_with?('-') ? opt[1..] : opt}".upcase
213
+ raise Cli::BadArgument, "Unknown ssl_option: #{name}, use one of: #{OpenSSL::SSL.constants.grep(/^OP_/).map{ |c| c.to_s.sub(/^OP_/, '')}.join(', ')}" if !OpenSSL::SSL.const_defined?(name)
214
+ if opt.start_with?('-')
215
+ ssl_options &= ~OpenSSL::SSL.const_get(name)
216
+ else
217
+ ssl_options |= OpenSSL::SSL.const_get(name)
218
+ end
219
+ else
220
+ Aspera.error_unexpected_value(opt.class.name){'Expected String or Integer in ssl_options'}
221
+ end
222
+ end
223
+ OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:options] = ssl_options
224
+ elsif OAuth::Factory.instance.parameters.key?(k.to_sym)
225
+ keys_to_delete.push(k)
226
+ OAuth::Factory.instance.parameters[k.to_sym] = v
227
+ end
256
228
  end
229
+ keys_to_delete.each{ |k| @option_http_options.delete(k)}
257
230
  OAuth::Factory.instance.persist_mgr = persistency if @option_cache_tokens
258
- OAuth::Web.additionnal_info = "#{Info::CMD_NAME} v#{Cli::VERSION}"
231
+ OAuth::Web.additional_info = "#{Info::CMD_NAME} v#{Cli::VERSION}"
259
232
  Transfer::Parameters.file_list_folder = File.join(@main_folder, 'filelists')
260
233
  RestErrorAnalyzer.instance.log_file = File.join(@main_folder, 'rest_exceptions.log')
261
- # register aspera REST call error handlers
234
+ # Register aspera REST call error handlers
262
235
  RestErrorsAspera.register_handlers
263
236
  end
264
237
 
265
238
  attr_accessor :main_folder, :option_cache_tokens, :option_insecure, :option_warn_insecure_cert, :option_http_options
266
239
  attr_reader :option_ignore_cert_host_port, :progress_bar
267
240
 
268
- # add files, folders or default locations to the certificate store
241
+ # Add files, folders or default locations to the certificate store
269
242
  # @param path_list [Array<String>] list of paths to add
270
243
  # @return the list of paths
271
244
  def trusted_cert_locations=(path_list)
@@ -307,14 +280,14 @@ module Aspera
307
280
  @certificate_paths.uniq!
308
281
  end
309
282
 
310
- # returns only files
283
+ # @return only files
311
284
  def trusted_cert_locations
312
285
  locations = @certificate_paths
313
286
  if locations.nil?
314
- # compute default locations
287
+ # Compute default locations
315
288
  self.trusted_cert_locations = SpecialValues::DEF
316
289
  locations = @certificate_paths
317
- # restore defaults
290
+ # Restore defaults
318
291
  @certificate_paths = @certificate_store = nil
319
292
  end
320
293
  return locations
@@ -357,7 +330,7 @@ module Aspera
357
330
  return ignore_cert
358
331
  end
359
332
 
360
- # called every time a new REST HTTP session is opened to set user-provided options
333
+ # Called every time a new REST HTTP session is opened to set user-provided options
361
334
  # @param http_session [Net::HTTP] the newly created HTTP/S session object
362
335
  def update_http_session(http_session)
363
336
  http_session.set_debug_output(LineLogger.new(:trace2)) if Log.instance.logger.trace2?
@@ -367,38 +340,12 @@ module Aspera
367
340
  Log.log.debug{"Using cert store #{http_session.cert_store} (#{@certificate_store})"} unless http_session.cert_store.nil?
368
341
  @option_http_options.each do |k, v|
369
342
  method = "#{k}=".to_sym
370
- # check if accessor is a method of Net::HTTP
343
+ # Check if accessor is a method of Net::HTTP
371
344
  # continue_timeout= read_timeout= write_timeout=
372
345
  if http_session.respond_to?(method)
373
346
  http_session.send(method, v)
374
- elsif k.eql?('ssl_options')
375
- # NOTE: here is a hack that allows setting SSLContext options
376
- Aspera.assert_type(v, Array){'ssl_options'}
377
- # more dynamic method, but more complex:
378
- # Net::HTTP::SSL_ATTRIBUTES.push(:options) unless Net::HTTP::SSL_ATTRIBUTES.include?(:options)
379
- # Net::HTTP::SSL_IVNAMES.push(:@options) unless Net::HTTP::SSL_IVNAMES.include?(:@options)
380
- # Start with default options
381
- ssl_options = OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:options]
382
- v.each do |opt|
383
- case opt
384
- when Integer
385
- ssl_options = opt
386
- when String
387
- name = "OP_#{opt.start_with?('-') ? opt[1..] : opt}".upcase
388
- raise Cli::BadArgument, "No such ssl_option: #{name}, use one of: #{OpenSSL::SSL.constants.grep(/^OP_/).map{ |c| c.to_s.sub(/^OP_/, '')}.join(', ')}" if !OpenSSL::SSL.const_defined?(name)
389
- if opt.start_with?('-')
390
- ssl_options &= ~OpenSSL::SSL.const_get(name)
391
- else
392
- ssl_options |= OpenSSL::SSL.const_get(name)
393
- end
394
- else
395
- Aspera.error_unexpected_value(opt.class.name){'Expected String or Integer in ssl_options'}
396
- end
397
- end
398
- # http_session.instance_variable_set(:@options, ssl_options)
399
- OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:options] = ssl_options
400
347
  else
401
- Log.log.error{"no such HTTP session attribute: #{k}"}
348
+ Log.log.error{"Unknown HTTP session attribute: #{k}"}
402
349
  end
403
350
  end
404
351
  end
@@ -426,35 +373,35 @@ module Aspera
426
373
  end
427
374
 
428
375
  def periodic_check_newer_gem_version
429
- # get verification period
376
+ # Get verification period
430
377
  delay_days = options.get_option(:version_check_days, mandatory: true).to_i
431
- # check only if not zero day
378
+ # Check only if not zero day
432
379
  return if delay_days.eql?(0)
433
- # get last date from persistency
380
+ # Get last date from persistency
434
381
  last_check_array = []
435
382
  check_date_persist = PersistencyActionOnce.new(
436
383
  manager: persistency,
437
384
  data: last_check_array,
438
385
  id: 'version_last_check'
439
386
  )
440
- # get persisted date or nil
387
+ # Get persisted date or nil
441
388
  current_date = Date.today
442
389
  last_check_days = (current_date - Date.strptime(last_check_array.first, GEM_CHECK_DATE_FMT)) rescue nil
443
390
  Log.log.debug{"gem check new version: #{delay_days}, #{last_check_days}, #{current_date}, #{last_check_array}"}
444
391
  return if !last_check_days.nil? && last_check_days < delay_days
445
- # generate timestamp
392
+ # Generate timestamp
446
393
  last_check_array[0] = current_date.strftime(GEM_CHECK_DATE_FMT)
447
394
  check_date_persist.save
448
- # compare this version and the one on internet
395
+ # Compare this version and the one on internet
449
396
  check_data = check_gem_version
450
397
  Log.log.warn do
451
398
  "A new version is available: #{check_data[:latest]}. You have #{check_data[:current]}. Upgrade with: gem update #{check_data[:name]}"
452
399
  end if check_data[:need_update]
453
400
  end
454
401
 
455
- # loads default parameters of plugin if no -P parameter
402
+ # Loads default parameters of plugin if no -P parameter
456
403
  # and if there is a section defined for the plugin in the "default" section
457
- # try to find: conf[conf["default"][plugin_str]]
404
+ # Try to find: conf[conf["default"][plugin_str]]
458
405
  # @param plugin_name_sym : symbol for plugin name
459
406
  def add_plugin_default_preset(plugin_name_sym)
460
407
  default_config_name = get_plugin_default_config_name(plugin_name_sym)
@@ -463,7 +410,7 @@ module Aspera
463
410
  return
464
411
  end
465
412
 
466
- # get the default global preset, or set default one
413
+ # Get the default global preset, or set default one
467
414
  def global_default_preset
468
415
  result = get_plugin_default_config_name(CONF_GLOBAL_SYM)
469
416
  if result.nil?
@@ -473,12 +420,25 @@ module Aspera
473
420
  return result
474
421
  end
475
422
 
423
+ def defaults_set(plugin_name, preset_name, preset_values, option_default, option_override)
424
+ @config_presets[CONF_PRESET_DEFAULTS] ||= {}
425
+ raise Cli::Error, "A default configuration already exists for plugin '#{plugin_name}' (use --override=yes or --default=no)" \
426
+ if !option_override && option_default && @config_presets[CONF_PRESET_DEFAULTS].key?(plugin_name)
427
+ raise Cli::Error, "Preset already exists: #{preset_name} (use --override=yes or provide alternate name on command line)" \
428
+ if !option_override && @config_presets.key?(preset_name)
429
+ if option_default
430
+ formatter.display_status("Setting config preset as default for #{plugin_name}")
431
+ @config_presets[CONF_PRESET_DEFAULTS][plugin_name.to_s] = preset_name
432
+ end
433
+ @config_presets[preset_name] = preset_values
434
+ end
435
+
476
436
  def set_preset_key(preset, param_name, param_value)
477
437
  Aspera.assert_values(param_name.class, [String, Symbol]){'parameter'}
478
438
  param_name = param_name.to_s
479
439
  selected_preset = @config_presets[preset]
480
440
  if selected_preset.nil?
481
- Log.log.debug{"No such preset name: #{preset}, initializing"}
441
+ Log.log.debug{"Unknown preset name: #{preset}, initializing"}
482
442
  selected_preset = @config_presets[preset] = {}
483
443
  end
484
444
  Aspera.assert_type(selected_preset, Hash){"#{preset}.#{param_name}"}
@@ -494,8 +454,8 @@ module Aspera
494
454
  nil
495
455
  end
496
456
 
497
- # set parameter and value in global config
498
- # creates one if none already created
457
+ # Set parameter and value in global config
458
+ # Creates one if none already created
499
459
  # @return preset name that contains global default
500
460
  def set_global_default(key, value)
501
461
  set_preset_key(global_default_preset, key, value)
@@ -505,18 +465,18 @@ module Aspera
505
465
  attr_reader :gem_url
506
466
  attr_accessor :option_config_file
507
467
 
508
- # @return the hash from name (also expands possible includes)
509
468
  # @param config_name name of the preset in config file
510
469
  # @param include_path used to detect and avoid include loops
470
+ # @return copy of the hash from name (also expands possible includes)
511
471
  def preset_by_name(config_name, include_path = [])
512
472
  raise Cli::Error, 'loop in include' if include_path.include?(config_name)
513
- include_path = include_path.clone # avoid messing up if there are multiple branches
473
+ include_path = include_path.clone # Avoid messing up if there are multiple branches
514
474
  current = @config_presets
515
475
  config_name.split(PRESET_DIG_SEPARATOR).each do |name|
516
476
  Aspera.assert_type(current, Hash, type: Cli::Error){"sub key: #{include_path}"}
517
477
  include_path.push(name)
518
478
  current = current[name]
519
- raise Cli::Error, "No such config preset: #{include_path}" if current.nil?
479
+ raise Cli::Error, "Unknown config preset: #{include_path}" if current.nil?
520
480
  end
521
481
  current = self.class.deep_clone(current) unless current.is_a?(String)
522
482
  return ExtendedValue.instance.evaluate(current)
@@ -534,11 +494,11 @@ module Aspera
534
494
  Aspera.assert_values(value.class, [String, Array]){'plugin folder'}
535
495
  value = [value] if value.is_a?(String)
536
496
  Aspera.assert(value.all?(String)){'plugin folder'}
537
- value.each{ |f| PluginFactory.instance.add_lookup_folder(f)}
497
+ value.each{ |f| Plugins::Factory.instance.add_lookup_folder(f)}
538
498
  end
539
499
 
540
500
  def option_plugin_folder
541
- return PluginFactory.instance.lookup_folders
501
+ return Plugins::Factory.instance.lookup_folders
542
502
  end
543
503
 
544
504
  def option_preset; 'write-only option'; end
@@ -558,14 +518,14 @@ module Aspera
558
518
  JSON.generate(@config_presets).hash
559
519
  end
560
520
 
561
- # read config file and validate format
521
+ # Read config file and validate format
562
522
  def read_config_file
563
523
  Log.log.debug{"config file is: #{@option_config_file}".red}
564
- # files search for configuration, by default the one given by user
524
+ # Files search for configuration, by default the one given by user
565
525
  search_files = [@option_config_file]
566
- # find first existing file (or nil)
526
+ # Find first existing file (or nil)
567
527
  conf_file_to_load = search_files.find{ |f| File.exist?(f)}
568
- # if no file found, create default config
528
+ # If no file found, create default config
569
529
  if conf_file_to_load.nil?
570
530
  Log.log.warn{"No config file found. New configuration file: #{@option_config_file}"}
571
531
  @config_presets = {CONF_PRESET_CONFIG => {CONF_PRESET_VERSION => 'new file'}}
@@ -578,16 +538,16 @@ module Aspera
578
538
  files_to_copy = []
579
539
  Log.dump(:available_presets, @config_presets, level: :trace1)
580
540
  Aspera.assert_type(@config_presets, Hash){'config file YAML'}
581
- # check there is at least the config section
541
+ # Check there is at least the config section
582
542
  Aspera.assert(@config_presets.key?(CONF_PRESET_CONFIG)){"Cannot find key: #{CONF_PRESET_CONFIG}"}
583
543
  version = @config_presets[CONF_PRESET_CONFIG][CONF_PRESET_VERSION]
584
544
  raise Error, 'No version found in config section.' if version.nil?
585
545
  Log.log.debug{"conf version: #{version}"}
586
546
  # VVV if there are any conversion needed, those happen here.
587
- # fix bug in 4.4 (creating key "true" in "default" preset)
547
+ # Fix bug in 4.4 (creating key "true" in "default" preset)
588
548
  @config_presets[CONF_PRESET_DEFAULTS].delete(true) if @config_presets[CONF_PRESET_DEFAULTS].is_a?(Hash)
589
549
  # ^^^ Place new compatibility code before this line
590
- # set version to current
550
+ # Set version to current
591
551
  @config_presets[CONF_PRESET_CONFIG][CONF_PRESET_VERSION] = Cli::VERSION
592
552
  unless files_to_copy.empty?
593
553
  Log.log.warn('Copying referenced files')
@@ -602,7 +562,7 @@ module Aspera
602
562
  rescue StandardError => e
603
563
  Log.log.debug{"-> #{e.class.name} : #{e}"}
604
564
  if File.exist?(@option_config_file)
605
- # then there is a problem with that file.
565
+ # Then there is a problem with that file.
606
566
  new_name = "#{@option_config_file}.pre#{Cli::VERSION}.manual_conversion_needed"
607
567
  File.rename(@option_config_file, new_name)
608
568
  Log.log.warn{"Renamed config file to #{new_name}."}
@@ -661,13 +621,13 @@ module Aspera
661
621
  when :show
662
622
  return Main.result_text(Ascp::Installation.instance.path(:ascp))
663
623
  when :info
664
- # collect info from ascp executable
624
+ # Collect info from ascp executable
665
625
  data = Ascp::Installation.instance.ascp_info
666
- # add command line transfer spec
626
+ # Add command line transfer spec
667
627
  data['ts'] = transfer.option_transfer_spec
668
- # add keys
628
+ # Add keys
669
629
  DataRepository::ELEMENTS.each_with_object(data){ |i, h| h[i.to_s] = DataRepository.instance.item(i)}
670
- # declare those as secrets
630
+ # Declare those as secrets
671
631
  SecretHider::ADDITIONAL_KEYS_TO_HIDE.concat(DataRepository::ELEMENTS.map(&:to_s))
672
632
  return Main.result_single_object(data)
673
633
  when :products
@@ -682,13 +642,13 @@ module Aspera
682
642
  return Main.result_nothing
683
643
  end
684
644
  when :install
685
- # reset to default location, if older default was used
686
- Products::Transferd.sdk_directory = self.class.default_app_main_folder(app_name: DIR_SDK) if @sdk_default_location
645
+ # Reset to default location, if older default was used
646
+ Products::Transferd.sdk_directory = self.class.default_app_main_folder(app_name: TRANSFERD_APP_NAME) if @sdk_default_location
687
647
  version = options.get_next_argument('transferd version', mandatory: false)
688
648
  n, v = Ascp::Installation.instance.install_sdk(url: options.get_option(:sdk_url, mandatory: true), version: version)
689
649
  return Main.result_status("Installed #{n} version #{v}")
690
650
  when :spec
691
- fields, data = Transfer::SpecDoc.man_table(Formatter)
651
+ fields, data = Transfer::SpecDoc.man_table(Formatter, include_option: true)
692
652
  return Main.result_object_list(data, fields: fields.map(&:to_s))
693
653
  when :schema
694
654
  schema = Transfer::Spec::SCHEMA.merge({'$comment'=>'DO NOT EDIT, this file was generated from the YAML.'})
@@ -698,7 +658,7 @@ module Aspera
698
658
  return Main.result_single_object(schema)
699
659
  when :errors
700
660
  error_data = []
701
- Transfer::ERROR_INFO.each_pair do |code, prop|
661
+ Ascp::Management::ERRORS.each_pair do |code, prop|
702
662
  error_data.push(code: code, mnemonic: prop[:c], retry: prop[:r], info: prop[:a])
703
663
  end
704
664
  return Main.result_object_list(error_data)
@@ -711,8 +671,8 @@ module Aspera
711
671
  command = options.get_next_command(%i[list install])
712
672
  case command
713
673
  when :install
714
- # reset to default location, if older default was used
715
- Products::Transferd.sdk_directory = self.class.default_app_main_folder(app_name: DIR_SDK) if @sdk_default_location
674
+ # Reset to default location, if older default was used
675
+ Products::Transferd.sdk_directory = self.class.default_app_main_folder(app_name: TRANSFERD_APP_NAME) if @sdk_default_location
716
676
  version = options.get_next_argument('transferd version', mandatory: false)
717
677
  n, v = Ascp::Installation.instance.install_sdk(url: options.get_option(:sdk_url, mandatory: true), version: version)
718
678
  return Main.result_status("Installed #{n} version #{v}")
@@ -727,9 +687,9 @@ module Aspera
727
687
  Aspera.error_unreachable_line
728
688
  end
729
689
 
730
- # legacy actions available globally
690
+ # Legacy actions available globally
731
691
  PRESET_GBL_ACTIONS = %i[list overview lookup secure].freeze
732
- # operations requiring that preset exists
692
+ # Operations requiring that preset exists
733
693
  PRESET_EXIST_ACTIONS = %i[show delete get unset].freeze
734
694
  # require id
735
695
  PRESET_INSTANCE_ACTIONS = %i[initialize update ask set].concat(PRESET_EXIST_ACTIONS).freeze
@@ -739,13 +699,13 @@ module Aspera
739
699
  action = options.get_next_command(PRESET_ALL_ACTIONS) if action.nil?
740
700
  name = instance_identifier if name.nil? && PRESET_INSTANCE_ACTIONS.include?(action)
741
701
  name = global_default_preset if name.eql?(GLOBAL_DEFAULT_KEYWORD)
742
- # those operations require existing option
702
+ # Those operations require existing option
743
703
  raise "no such preset: #{name}" if PRESET_EXIST_ACTIONS.include?(action) && !@config_presets.key?(name)
744
704
  case action
745
705
  when :list
746
706
  return Main.result_value_list(@config_presets.keys, name: 'name')
747
707
  when :overview
748
- # display process modifies the value (hide secrets): we do not want to save removed secrets
708
+ # Display process modifies the value (hide secrets): we do not want to save removed secrets
749
709
  data = self.class.deep_clone(@config_presets)
750
710
  formatter.hide_secrets(data)
751
711
  result = []
@@ -799,7 +759,7 @@ module Aspera
799
759
  end
800
760
  return Main.result_status("Updated: #{name}")
801
761
  when :lookup
802
- BasicAuthPlugin.declare_options(options)
762
+ BasicAuth.declare_options(options)
803
763
  url = options.get_option(:url, mandatory: true)
804
764
  user = options.get_option(:username, mandatory: true)
805
765
  result = lookup_preset(url: url, username: user)
@@ -850,6 +810,7 @@ module Aspera
850
810
  coffee
851
811
  image
852
812
  ascp
813
+ sync
853
814
  transferd
854
815
  email_test
855
816
  smtp_settings
@@ -867,7 +828,7 @@ module Aspera
867
828
  def execute_action
868
829
  action = options.get_next_command(ACTIONS)
869
830
  case action
870
- when :preset # newer syntax
831
+ when :preset # Newer syntax
871
832
  return execute_preset
872
833
  when :open
873
834
  Environment.instance.open_editor(@option_config_file.to_s)
@@ -877,12 +838,12 @@ module Aspera
877
838
  section = "##{section}" unless section.nil?
878
839
  Environment.instance.open_uri("#{Info::DOC_URL}#{section}")
879
840
  return Main.result_nothing
880
- when :genkey # generate new rsa key
841
+ when :genkey # Generate new rsa key
881
842
  private_key_path = options.get_next_argument('private key file path')
882
843
  private_key_length = options.get_next_argument('size in bits', mandatory: false, validation: Integer, default: OAuth::Jwt::DEFAULT_PRIV_KEY_LENGTH)
883
844
  OAuth::Jwt.generate_rsa_private_key(path: private_key_path, length: private_key_length)
884
845
  return Main.result_status("Generated #{private_key_length} bit RSA key: #{private_key_path}")
885
- when :pubkey # get pub key
846
+ when :pubkey # Get pub key
886
847
  private_key_pem = options.get_next_argument('private key PEM value')
887
848
  return Main.result_text(OpenSSL::PKey::RSA.new(private_key_pem).public_key.to_s)
888
849
  when :remote_certificate
@@ -898,7 +859,7 @@ module Aspera
898
859
  when :name
899
860
  return Main.result_text(remote_chain.first.subject.to_a.find{ |name, _, _| name == 'CN'}[1])
900
861
  end
901
- when :echo # display the content of a value given on command line
862
+ when :echo # Display the content of a value given on command line
902
863
  return Main.result_auto(options.get_next_argument('value', validation: nil))
903
864
  when :download
904
865
  file_url = options.get_next_argument('source URL').chomp
@@ -916,20 +877,20 @@ module Aspera
916
877
  return Main.result_object_list(OAuth::Factory.instance.persisted_tokens)
917
878
  when :show
918
879
  data = OAuth::Factory.instance.get_token_info(instance_identifier)
919
- raise Cli::Error, 'No such identifier' if data.nil?
880
+ raise Cli::Error, 'Unknown identifier' if data.nil?
920
881
  return Main.result_single_object(data)
921
882
  end
922
883
  when :plugins
923
884
  case options.get_next_command(%i[list create])
924
885
  when :list
925
886
  result = []
926
- PluginFactory.instance.plugin_list.each do |name|
927
- plugin_class = PluginFactory.instance.plugin_class(name)
887
+ Plugins::Factory.instance.plugin_list.each do |name|
888
+ plugin_class = Plugins::Factory.instance.plugin_class(name)
928
889
  result.push({
929
890
  plugin: name,
930
891
  detect: Formatter.tick(plugin_class.respond_to?(:detect)),
931
- wizard: Formatter.tick(plugin_class.respond_to?(:wizard)),
932
- path: PluginFactory.instance.plugin_source(name)
892
+ wizard: Formatter.tick(plugin_class.instance_methods.include?(:wizard)),
893
+ path: Plugins::Factory.instance.plugin_source(name)
933
894
  })
934
895
  end
935
896
  return Main.result_object_list(result, fields: %w[plugin detect wizard path])
@@ -938,25 +899,27 @@ module Aspera
938
899
  destination_folder = options.get_next_argument('folder', mandatory: false) || File.join(@main_folder, ASPERA_PLUGINS_FOLDERNAME)
939
900
  plugin_file = File.join(destination_folder, "#{plugin_name}.rb")
940
901
  content = <<~END_OF_PLUGIN_CODE
941
- require 'aspera/cli/plugin'
902
+ require 'aspera/cli/plugins/base'
942
903
  module Aspera
943
904
  module Cli
944
905
  module Plugins
945
- class #{plugin_name.capitalize} < Plugin
906
+ class #{plugin_name.snake_to_capital} < Base
946
907
  ACTIONS=[]
947
- def execute_action; return Main.result_status('You called plugin #{plugin_name}'); end
948
- end # #{plugin_name.capitalize}
949
- end # Plugins
950
- end # Cli
951
- end # Aspera
908
+ def execute_action
909
+ return Main.result_status('You called plugin #{plugin_name}')
910
+ end
911
+ end
912
+ end
913
+ end
914
+ end
952
915
  END_OF_PLUGIN_CODE
953
916
  File.write(plugin_file, content)
954
917
  return Main.result_status("Created #{plugin_file}")
955
918
  end
956
919
  when :detect, :wizard
957
- # interactive mode
920
+ # Interactive mode
958
921
  options.ask_missing_mandatory = true
959
- # detect plugins by url and optional query
922
+ # Detect plugins by url and optional query
960
923
  apps = @wizard.identify_plugins_for_url.freeze
961
924
  return Main.result_object_list(apps) if action.eql?(:detect)
962
925
  return @wizard.find(apps)
@@ -966,6 +929,13 @@ module Aspera
966
929
  return Main.result_image(options.get_next_argument('image URI or blob'))
967
930
  when :ascp
968
931
  execute_action_ascp
932
+ when :sync
933
+ case options.get_next_command(%i[spec])
934
+ when :spec
935
+ fields, data = Transfer::SpecDoc.man_table(Formatter, include_option: true, agent_columns: false, schema: Sync::Operations::CONF_SCHEMA)
936
+ return Main.result_object_list(data, fields: fields.map(&:to_s))
937
+ else Aspera.error_unreachable_line
938
+ end
969
939
  when :transferd
970
940
  execute_action_transferd
971
941
  when :gem
@@ -973,6 +943,7 @@ module Aspera
973
943
  when :path then return Main.result_text(self.class.gem_src_root)
974
944
  when :version then return Main.result_text(Cli::VERSION)
975
945
  when :name then return Main.result_text(Info::GEM_NAME)
946
+ else Aspera.error_unreachable_line
976
947
  end
977
948
  when :folder
978
949
  return Main.result_text(@main_folder)
@@ -984,7 +955,7 @@ module Aspera
984
955
  when :smtp_settings
985
956
  return Main.result_single_object(email_settings)
986
957
  when :proxy_check
987
- # ensure fpac was provided
958
+ # Ensure fpac was provided
988
959
  options.get_option(:fpac, mandatory: true)
989
960
  server_url = options.get_next_argument('server url')
990
961
  return Main.result_text(@pac_exec.get_proxies(server_url))
@@ -1022,11 +993,11 @@ module Aspera
1022
993
  # @return [Hash] email server setting with defaults if not defined
1023
994
  def email_settings
1024
995
  smtp = options.get_option(:smtp, mandatory: true)
1025
- # change keys from string into symbol
996
+ # Change keys from string into symbol
1026
997
  smtp = smtp.symbolize_keys
1027
998
  unsupported = smtp.keys - SMTP_CONF_PARAMS
1028
999
  raise Cli::Error, "Unsupported SMTP parameter: #{unsupported.join(', ')}, use: #{SMTP_CONF_PARAMS.join(', ')}" unless unsupported.empty?
1029
- # defaults
1000
+ # Defaults
1030
1001
  # smtp[:ssl] = nil (false)
1031
1002
  smtp[:tls] = !smtp[:ssl] unless smtp.key?(:tls)
1032
1003
  smtp[:port] ||= if smtp[:tls]
@@ -1039,7 +1010,7 @@ module Aspera
1039
1010
  smtp[:from_email] ||= smtp[:username] if smtp.key?(:username)
1040
1011
  smtp[:from_name] ||= smtp[:from_email].sub(/@.*$/, '').gsub(/[^a-zA-Z]/, ' ').capitalize if smtp.key?(:username)
1041
1012
  smtp[:domain] ||= smtp[:from_email].sub(/^.*@/, '') if smtp.key?(:from_email)
1042
- # check minimum required
1013
+ # Check minimum required
1043
1014
  %i[server port domain].each do |n|
1044
1015
  Aspera.assert(smtp.key?(n)){"Missing mandatory smtp parameter: #{n}"}
1045
1016
  end
@@ -1047,8 +1018,8 @@ module Aspera
1047
1018
  return smtp
1048
1019
  end
1049
1020
 
1050
- # send email using ERB template
1051
- # @param email_template_default [String] default template, can be overriden by option
1021
+ # Send email using ERB template
1022
+ # @param email_template_default [String] default template, can be overridden by option
1052
1023
  # @param values [Hash] values to be used in template, keys with default: to, from_name, from_email
1053
1024
  def send_email_template(email_template_default: nil, values: {})
1054
1025
  values[:to] ||= options.get_option(:notify_to, mandatory: true)
@@ -1061,14 +1032,14 @@ module Aspera
1061
1032
  end
1062
1033
  start_options = [mail_conf[:domain]]
1063
1034
  start_options.push(mail_conf[:username], mail_conf[:password], :login) if mail_conf.key?(:username) && mail_conf.key?(:password)
1064
- # create a binding with only variables defined in values
1035
+ # Create a binding with only variables defined in values
1065
1036
  template_binding = Environment.empty_binding
1066
- # add variables to binding
1037
+ # Add variables to binding
1067
1038
  values.each do |k, v|
1068
1039
  Aspera.assert_type(k, Symbol)
1069
1040
  template_binding.local_variable_set(k, v)
1070
1041
  end
1071
- # execute template
1042
+ # Execute template
1072
1043
  msg_with_headers = ERB.new(notify_template).result(template_binding)
1073
1044
  Log.dump(:msg_with_headers, msg_with_headers)
1074
1045
  require 'net/smtp'
@@ -1082,7 +1053,7 @@ module Aspera
1082
1053
  end
1083
1054
 
1084
1055
  # Save current configuration to config file
1085
- # return true if file was saved
1056
+ # @return true if file was saved
1086
1057
  def save_config_file_if_needed
1087
1058
  raise Error, 'no configuration loaded' if @config_presets.nil?
1088
1059
  current_checksum = config_checksum
@@ -1096,29 +1067,35 @@ module Aspera
1096
1067
  return true
1097
1068
  end
1098
1069
 
1099
- # returns [String] name if config_presets has default
1100
- # returns nil if there is no config or bypass default params
1070
+ # @return [String] name if config_presets has default
1071
+ # @return nil if there is no config or bypass default params
1101
1072
  def get_plugin_default_config_name(plugin_name_sym)
1102
1073
  Aspera.assert(!@config_presets.nil?){'config_presets shall be defined'}
1103
1074
  if !@use_plugin_defaults
1104
1075
  Log.log.debug('skip default config')
1105
1076
  return
1106
1077
  end
1107
- if @config_presets.key?(CONF_PRESET_DEFAULTS) &&
1108
- @config_presets[CONF_PRESET_DEFAULTS].key?(plugin_name_sym.to_s)
1109
- default_config_name = @config_presets[CONF_PRESET_DEFAULTS][plugin_name_sym.to_s]
1110
- if !@config_presets.key?(default_config_name)
1111
- Log.log.error do
1112
- "Default config name [#{default_config_name}] specified for plugin [#{plugin_name_sym}], but it does not exist in config file.\n" \
1113
- 'Please fix the issue: either create preset with one parameter: ' \
1114
- "(#{Info::CMD_NAME} config id #{default_config_name} init @json:'{}') " \
1115
- "or remove default (#{Info::CMD_NAME} config id default remove #{plugin_name_sym})."
1116
- end
1078
+ if !@config_presets.key?(CONF_PRESET_DEFAULTS)
1079
+ Log.log.debug('No default section')
1080
+ return
1081
+ end
1082
+ Aspera.assert_type(@config_presets[CONF_PRESET_DEFAULTS], Hash){'default section'}
1083
+ if !@config_presets[CONF_PRESET_DEFAULTS].key?(plugin_name_sym.to_s)
1084
+ Log.log.debug("No default for #{plugin_name_sym}")
1085
+ return
1086
+ end
1087
+ default_config_name = @config_presets[CONF_PRESET_DEFAULTS][plugin_name_sym.to_s]
1088
+ if !@config_presets.key?(default_config_name)
1089
+ Log.log.error do
1090
+ "Default config name [#{default_config_name}] specified for plugin [#{plugin_name_sym}], but it does not exist in config file.\n" \
1091
+ "Please fix the issue: either create preset with one parameter:\n" \
1092
+ "#{Info::CMD_NAME} config id #{default_config_name} init @json:'{}'\n" \
1093
+ "or remove default:\n#{Info::CMD_NAME} config id default remove #{plugin_name_sym}"
1117
1094
  end
1118
- raise Cli::Error, "Config name [#{default_config_name}] must be a hash, check config file." if !@config_presets[default_config_name].is_a?(Hash)
1119
- return default_config_name
1095
+ raise Cli::Error, "No such preset: #{default_config_name}"
1120
1096
  end
1121
- return
1097
+ Aspera.assert_type(@config_presets[default_config_name], Hash, type: Cli::Error){'preset type'}
1098
+ return default_config_name
1122
1099
  end
1123
1100
 
1124
1101
  # @return [Hash] result of execution of vault command
@@ -1150,7 +1127,7 @@ module Aspera
1150
1127
  def vault_value(name)
1151
1128
  m = name.split('.')
1152
1129
  raise BadArgument, 'vault name shall match <name>.<param>' unless m.length.eql?(2)
1153
- # this raise exception if label not found:
1130
+ # This raise exception if label not found:
1154
1131
  info = vault.get(label: m[0])
1155
1132
  value = info[m[1].to_sym]
1156
1133
  raise "no such entry value: #{m[1]}" if value.nil?
@@ -1171,12 +1148,12 @@ module Aspera
1171
1148
  )
1172
1149
  end
1173
1150
 
1174
- # Artifically raise an exception for tests
1151
+ # Artificially raise an exception for tests
1175
1152
  def execute_test
1176
1153
  case options.get_next_command(%i[throw web])
1177
1154
  when :throw
1178
1155
  # :type [String]
1179
- # options
1156
+ # Options
1180
1157
  exception_class_name = options.get_next_argument('exception class name', mandatory: true)
1181
1158
  exception_text = options.get_next_argument('exception text', mandatory: true)
1182
1159
  type = Object.const_get(exception_class_name)
@@ -1186,7 +1163,7 @@ module Aspera
1186
1163
  end
1187
1164
  end
1188
1165
 
1189
- # version of URL without trailing "/" and removing default port
1166
+ # Version of URL without trailing "/" and removing default port
1190
1167
  def canonical_url(url)
1191
1168
  url.chomp('/').sub(%r{^(https://[^/]+):443$}, '\1')
1192
1169
  end
@@ -1194,7 +1171,7 @@ module Aspera
1194
1171
  # Look for a preset that has the corresponding URL and username
1195
1172
  # @return the first one matching
1196
1173
  def lookup_preset(url:, username:)
1197
- # remove extra info to maximize match
1174
+ # Remove extra info to maximize match
1198
1175
  url = canonical_url(url)
1199
1176
  Log.log.debug{"Lookup preset for #{username}@#{url}"}
1200
1177
  @config_presets.each_value do |v|
@@ -1219,6 +1196,71 @@ module Aspera
1219
1196
  end
1220
1197
  return secret
1221
1198
  end
1199
+ # Private
1200
+ # Folder in $HOME for application files (config, cache)
1201
+ ASPERA_HOME_FOLDER_NAME = '.aspera'
1202
+ # Default config file
1203
+ DEFAULT_CONFIG_FILENAME = 'config.yaml'
1204
+ # Reserved preset names
1205
+ CONF_PRESET_CONFIG = 'config'
1206
+ CONF_PRESET_VERSION = 'version'
1207
+ CONF_PRESET_DEFAULTS = 'default'
1208
+ CONF_PRESET_GLOBAL = 'global_common_defaults'
1209
+ # Special name to identify value of default
1210
+ GLOBAL_DEFAULT_KEYWORD = 'GLOBAL'
1211
+ CONF_GLOBAL_SYM = :config
1212
+ # Folder containing custom plugins in user's config folder
1213
+ ASPERA_PLUGINS_FOLDERNAME = 'plugins'
1214
+ PERSISTENCY_FOLDER = 'persist_store'
1215
+ ASPERA = 'aspera'
1216
+ SERVER_COMMAND = 'server'
1217
+ TRANSFERD_APP_NAME = 'sdk'
1218
+ DEMO_SERVER = 'demo'
1219
+ DEMO_PRESET = 'demoserver' # cspell: disable-line
1220
+ EMAIL_TEST_TEMPLATE = <<~END_OF_TEMPLATE
1221
+ From: <%=from_name%> <<%=from_email%>>
1222
+ To: <<%=to%>>
1223
+ Subject: #{Info::GEM_NAME} email test
1224
+
1225
+ This email was sent to test #{Info::CMD_NAME}.
1226
+ END_OF_TEMPLATE
1227
+ # Special extended values
1228
+ EXTEND_PRESET = :preset
1229
+ EXTEND_VAULT = :vault
1230
+ PRESET_DIG_SEPARATOR = '.'
1231
+ DEFAULT_CHECK_NEW_VERSION_DAYS = 7
1232
+ COFFEE_IMAGE_URL = 'https://enjoyjava.com/wp-content/uploads/2018/01/How-to-make-strong-coffee.jpg'
1233
+ GEM_CHECK_DATE_FMT = '%Y/%m/%d'
1234
+ # For testing only
1235
+ SELF_SIGNED_CERT = OpenSSL::SSL.const_get(:enon_yfirev.to_s.upcase.reverse) # cspell: disable-line
1236
+ CONF_OVERVIEW_KEYS = %w[preset parameter value].freeze
1237
+ SMTP_CONF_PARAMS = %i[server tls ssl port domain username password from_name from_email].freeze
1238
+
1239
+ private_constant :ASPERA_HOME_FOLDER_NAME,
1240
+ :DEFAULT_CONFIG_FILENAME,
1241
+ :CONF_PRESET_CONFIG,
1242
+ :CONF_PRESET_VERSION,
1243
+ :CONF_PRESET_DEFAULTS,
1244
+ :CONF_PRESET_GLOBAL,
1245
+ :ASPERA_PLUGINS_FOLDERNAME,
1246
+ :ASPERA,
1247
+ :DEMO_SERVER,
1248
+ :DEMO_PRESET,
1249
+ :EMAIL_TEST_TEMPLATE,
1250
+ :EXTEND_PRESET,
1251
+ :EXTEND_VAULT,
1252
+ :DEFAULT_CHECK_NEW_VERSION_DAYS,
1253
+ :SERVER_COMMAND,
1254
+ :PRESET_DIG_SEPARATOR,
1255
+ :COFFEE_IMAGE_URL,
1256
+ :SELF_SIGNED_CERT,
1257
+ :PERSISTENCY_FOLDER,
1258
+ :CONF_OVERVIEW_KEYS,
1259
+ :SMTP_CONF_PARAMS,
1260
+ :TRANSFERD_APP_NAME,
1261
+ :GLOBAL_DEFAULT_KEYWORD,
1262
+ :CONF_GLOBAL_SYM,
1263
+ :GEM_CHECK_DATE_FMT
1222
1264
  end
1223
1265
  end
1224
1266
  end