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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +19 -1
- data/CONTRIBUTING.md +2 -5
- data/bin/ascli +2 -2
- data/lib/aspera/agent/direct.rb +1 -1
- data/lib/aspera/agent/factory.rb +4 -0
- data/lib/aspera/agent/httpgw.rb +1 -1
- data/lib/aspera/agent/node.rb +2 -2
- data/lib/aspera/api/aoc.rb +43 -36
- data/lib/aspera/api/cos_node.rb +1 -1
- data/lib/aspera/api/faspex.rb +7 -6
- data/lib/aspera/api/node.rb +7 -7
- data/lib/aspera/ascmd.rb +4 -4
- data/lib/aspera/ascp/installation.rb +6 -2
- data/lib/aspera/cli/context.rb +58 -0
- data/lib/aspera/cli/extended_value.rb +3 -2
- data/lib/aspera/cli/formatter.rb +83 -184
- data/lib/aspera/cli/http.rb +167 -0
- data/lib/aspera/cli/manager.rb +3 -2
- data/lib/aspera/cli/plugins/alee.rb +2 -2
- data/lib/aspera/cli/plugins/aoc.rb +78 -67
- data/lib/aspera/cli/plugins/ats.rb +16 -16
- data/lib/aspera/cli/plugins/base.rb +18 -12
- data/lib/aspera/cli/plugins/config.rb +166 -438
- data/lib/aspera/cli/plugins/console.rb +7 -7
- data/lib/aspera/cli/plugins/cos.rb +2 -2
- data/lib/aspera/cli/plugins/factory.rb +3 -0
- data/lib/aspera/cli/plugins/faspex.rb +21 -21
- data/lib/aspera/cli/plugins/faspex5.rb +64 -44
- data/lib/aspera/cli/plugins/faspio.rb +1 -1
- data/lib/aspera/cli/plugins/httpgw.rb +2 -2
- data/lib/aspera/cli/plugins/node.rb +79 -79
- data/lib/aspera/cli/plugins/orchestrator.rb +21 -26
- data/lib/aspera/cli/plugins/preview.rb +9 -9
- data/lib/aspera/cli/plugins/server.rb +7 -7
- data/lib/aspera/cli/plugins/shares.rb +2 -2
- data/lib/aspera/cli/preset_manager.rb +235 -0
- data/lib/aspera/cli/result.rb +310 -0
- data/lib/aspera/cli/{main.rb → runner.rb} +14 -155
- data/lib/aspera/cli/sync_actions.rb +14 -10
- data/lib/aspera/cli/terminal_formatter.rb +65 -0
- data/lib/aspera/cli/transfer_progress.rb +6 -6
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/cli/wizard.rb +5 -6
- data/lib/aspera/command_line_builder.rb +1 -1
- data/lib/aspera/data_repository.rb +4 -0
- data/lib/aspera/dot_container.rb +1 -1
- data/lib/aspera/environment.rb +22 -3
- data/lib/aspera/faspex_gw.rb +1 -1
- data/lib/aspera/json_rpc.rb +6 -5
- data/lib/aspera/keychain/base.rb +1 -1
- data/lib/aspera/keychain/encrypted_hash.rb +1 -1
- data/lib/aspera/keychain/factory.rb +1 -1
- data/lib/aspera/keychain/macos_security.rb +1 -1
- data/lib/aspera/log.rb +6 -2
- data/lib/aspera/nagios.rb +2 -2
- data/lib/aspera/oauth/base.rb +5 -5
- data/lib/aspera/oauth/boot.rb +43 -0
- data/lib/aspera/oauth/factory.rb +37 -13
- data/lib/aspera/oauth/web.rb +2 -2
- data/lib/aspera/oauth.rb +1 -0
- data/lib/aspera/persistency_action_once.rb +2 -2
- data/lib/aspera/preview/file_types.rb +4 -0
- data/lib/aspera/products/connect.rb +3 -0
- data/lib/aspera/products/transferd.rb +2 -2
- data/lib/aspera/proxy_auto_config.rb +3 -3
- data/lib/aspera/rest.rb +8 -4
- data/lib/aspera/rest_error_analyzer.rb +4 -0
- data/lib/aspera/rest_list.rb +10 -3
- data/lib/aspera/schema/IBM Aspera Faspex API-5.0-enhanced.yaml +14 -4
- data/lib/aspera/schema/IBM Aspera on Cloud API-0.2.6-enhanced.yaml +827 -88
- data/lib/aspera/schema/async_tables.yaml +361 -0
- data/lib/aspera/schema/reader.rb +1 -1
- data/lib/aspera/schema/registry.rb +13 -6
- data/lib/aspera/secret_hider.rb +4 -0
- data/lib/aspera/sync/database.rb +14 -10
- data/lib/aspera/sync/operations.rb +5 -5
- data/lib/aspera/temp_file_manager.rb +4 -0
- data/lib/aspera/transfer/parameters.rb +4 -4
- data/lib/aspera/transfer/resumer.rb +1 -1
- data/lib/aspera/transfer/spec.schema.yaml +17 -8
- data/lib/aspera/transfer/uri.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +21 -14
- 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
|
-
#
|
|
120
|
-
|
|
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'){
|
|
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
|
-
|
|
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|
|
|
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){
|
|
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
|
-
#
|
|
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
|
-
|
|
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|
|
|
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
|
|
223
|
-
attr_reader :
|
|
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
|
-
#
|
|
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 =
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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) &&
|
|
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
|
|
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 =
|
|
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
|
|
453
|
+
return Result::ObjectList.new(result, fields: CONF_OVERVIEW_KEYS)
|
|
664
454
|
when :show
|
|
665
|
-
return
|
|
455
|
+
return Result::SingleObject.new(PresetManager.deep_clone(cp[name]))
|
|
666
456
|
when :delete
|
|
667
|
-
|
|
668
|
-
return
|
|
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 =
|
|
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
|
|
464
|
+
when Numeric, String then return Result::Text.new(ExtendedValue.instance.evaluate(value.to_s, context: 'preset'))
|
|
675
465
|
end
|
|
676
|
-
return
|
|
466
|
+
return Result::SingleObject.new(value)
|
|
677
467
|
when :unset
|
|
678
468
|
param_name = options.get_next_argument('parameter name')
|
|
679
|
-
|
|
680
|
-
return
|
|
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
|
|
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
|
|
690
|
-
|
|
691
|
-
return
|
|
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
|
-
|
|
697
|
-
|
|
698
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
493
|
+
cp[name][option_name] = option_value
|
|
705
494
|
end
|
|
706
|
-
return
|
|
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
|
|
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? ?
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
591
|
+
return Result::Text.new(remote_chain.map(&:to_pem).join("\n"))
|
|
803
592
|
when :only
|
|
804
|
-
return
|
|
593
|
+
return Result::Text.new(remote_chain.first.to_pem)
|
|
805
594
|
when :name
|
|
806
|
-
return
|
|
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
|
|
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
|
|
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
|
|
610
|
+
return Result::ValueList.new(OAuth::Factory.instance.flush_tokens, name: 'file')
|
|
822
611
|
when :list
|
|
823
|
-
return
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
659
|
+
return Result::ObjectList.new(apps) if action.eql?(:detect)
|
|
871
660
|
return @wizard.find(apps)
|
|
872
661
|
when :coffee
|
|
873
|
-
return
|
|
662
|
+
return Result::Image.new(COFFEE_IMAGE_URL)
|
|
874
663
|
when :image
|
|
875
|
-
return
|
|
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
|
|
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
|
|
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
|
|
894
|
-
when :version then return
|
|
895
|
-
when :name then return
|
|
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
|
|
689
|
+
return Result::Text.new(@main_folder)
|
|
900
690
|
when :file
|
|
901
|
-
return
|
|
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
|
|
694
|
+
return Result::Nothing.new
|
|
905
695
|
when :smtp_settings
|
|
906
|
-
return
|
|
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
|
|
701
|
+
return Result::ValueList.new(@pac_exec.get_proxies(server_url), name: 'proxy')
|
|
912
702
|
when :check_update
|
|
913
|
-
return
|
|
703
|
+
return Result::SingleObject.new(check_gem_version)
|
|
914
704
|
when :initdemo
|
|
915
|
-
|
|
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
|
-
|
|
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
|
-
|
|
926
|
-
if
|
|
927
|
-
Log.log.warn{"Server default preset already set to: #{
|
|
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?(
|
|
720
|
+
DEMO_PRESET.eql?(cp[PresetManager::Key::DEFAULTS][SERVER_COMMAND])
|
|
930
721
|
else
|
|
931
|
-
|
|
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
|
|
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
|
|
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
|
|
819
|
+
return Result::SingleObject.new(vault.info)
|
|
1056
820
|
when :list
|
|
1057
821
|
# , fields: %w(label url username password description)
|
|
1058
|
-
return
|
|
822
|
+
return Result::ObjectList.new(vault.list)
|
|
1059
823
|
when :show
|
|
1060
|
-
return
|
|
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
|
|
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
|
|
831
|
+
return Result::Status.new("Secret deleted: #{label_to_delete}")
|
|
1068
832
|
when :password
|
|
1069
|
-
Aspera.assert(vault.respond_to?(:change_password)
|
|
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
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|
-
:
|
|
1195
|
-
:
|
|
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
|
-
:
|
|
946
|
+
:CONF_OVERVIEW_KEYS,
|
|
947
|
+
:SMTP_CONF_PARAMS
|
|
1220
948
|
end
|
|
1221
949
|
end
|
|
1222
950
|
end
|