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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +19 -1
- data/README.md +1264 -941
- data/bin/ascli +20 -1
- data/bin/asession +23 -27
- data/lib/aspera/agent/base.rb +10 -21
- data/lib/aspera/agent/connect.rb +2 -3
- data/lib/aspera/agent/desktop.rb +2 -2
- data/lib/aspera/agent/direct.rb +49 -32
- data/lib/aspera/agent/factory.rb +31 -0
- data/lib/aspera/api/aoc.rb +79 -49
- data/lib/aspera/api/faspex.rb +212 -0
- data/lib/aspera/api/node.rb +99 -84
- data/lib/aspera/ascp/installation.rb +22 -21
- data/lib/aspera/ascp/management.rb +119 -23
- data/lib/aspera/assert.rb +14 -8
- data/lib/aspera/cli/extended_value.rb +15 -15
- data/lib/aspera/cli/formatter.rb +7 -5
- data/lib/aspera/cli/hints.rb +8 -0
- data/lib/aspera/cli/info.rb +4 -4
- data/lib/aspera/cli/main.rb +56 -71
- data/lib/aspera/cli/manager.rb +7 -4
- data/lib/aspera/cli/plugins/alee.rb +2 -1
- data/lib/aspera/cli/plugins/aoc.rb +110 -186
- data/lib/aspera/cli/plugins/ats.rb +4 -4
- data/lib/aspera/cli/plugins/base.rb +335 -0
- data/lib/aspera/cli/plugins/basic_auth.rb +45 -0
- data/lib/aspera/cli/plugins/config.rb +263 -221
- data/lib/aspera/cli/plugins/console.rb +15 -15
- data/lib/aspera/cli/plugins/cos.rb +2 -2
- data/lib/aspera/cli/plugins/factory.rb +78 -0
- data/lib/aspera/cli/plugins/faspex.rb +17 -20
- data/lib/aspera/cli/plugins/faspex5.rb +79 -193
- data/lib/aspera/cli/plugins/faspio.rb +14 -13
- data/lib/aspera/cli/plugins/httpgw.rb +13 -12
- data/lib/aspera/cli/plugins/node.rb +34 -32
- data/lib/aspera/cli/plugins/oauth.rb +48 -0
- data/lib/aspera/cli/plugins/orchestrator.rb +15 -13
- data/lib/aspera/cli/plugins/preview.rb +4 -4
- data/lib/aspera/cli/plugins/server.rb +15 -13
- data/lib/aspera/cli/plugins/shares.rb +18 -15
- data/lib/aspera/cli/sync_actions.rb +1 -1
- data/lib/aspera/cli/transfer_agent.rb +24 -20
- data/lib/aspera/cli/transfer_progress.rb +6 -6
- data/lib/aspera/cli/version.rb +3 -3
- data/lib/aspera/cli/wizard.rb +74 -65
- data/lib/aspera/colors.rb +6 -0
- data/lib/aspera/command_line_builder.rb +45 -50
- data/lib/aspera/command_line_converter.rb +2 -1
- data/lib/aspera/coverage.rb +1 -1
- data/lib/aspera/data_repository.rb +1 -1
- data/lib/aspera/environment.rb +13 -9
- data/lib/aspera/faspex_gw.rb +6 -4
- data/lib/aspera/faspex_postproc.rb +1 -1
- data/lib/aspera/keychain/macos_security.rb +1 -1
- data/lib/aspera/log.rb +88 -37
- data/lib/aspera/nagios.rb +1 -1
- data/lib/aspera/oauth/base.rb +17 -10
- data/lib/aspera/oauth/factory.rb +8 -8
- data/lib/aspera/oauth/web.rb +2 -2
- data/lib/aspera/products/connect.rb +4 -3
- data/lib/aspera/products/desktop.rb +1 -4
- data/lib/aspera/products/other.rb +9 -1
- data/lib/aspera/products/transferd.rb +0 -1
- data/lib/aspera/rest.rb +126 -83
- data/lib/aspera/ssh.rb +3 -3
- data/lib/aspera/sync/args.schema.yaml +46 -3
- data/lib/aspera/sync/conf.schema.yaml +130 -94
- data/lib/aspera/sync/operations.rb +71 -74
- data/lib/aspera/temp_file_manager.rb +17 -5
- data/lib/aspera/transfer/error.rb +16 -7
- data/lib/aspera/transfer/parameters.rb +34 -20
- data/lib/aspera/transfer/resumer.rb +74 -0
- data/lib/aspera/transfer/spec.rb +4 -3
- data/lib/aspera/transfer/spec.schema.yaml +132 -51
- data/lib/aspera/transfer/spec_doc.rb +41 -35
- data/lib/aspera/uri_reader.rb +1 -1
- data/lib/aspera/web_auth.rb +6 -6
- data.tar.gz.sig +0 -0
- metadata +9 -7
- metadata.gz.sig +2 -2
- data/lib/aspera/cli/basic_auth_plugin.rb +0 -43
- data/lib/aspera/cli/plugin.rb +0 -333
- data/lib/aspera/cli/plugin_factory.rb +0 -81
- data/lib/aspera/resumer.rb +0 -77
- 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/
|
|
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
|
-
#
|
|
40
|
-
class Config <
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|
|
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
|
-
#
|
|
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
|
-
#
|
|
92
|
+
# Store is used for ruby https
|
|
151
93
|
@certificate_store = nil
|
|
152
|
-
#
|
|
94
|
+
# Paths are used for ascp
|
|
153
95
|
@certificate_paths = nil
|
|
154
96
|
@progress_bar = nil
|
|
155
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
#
|
|
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
|
-
#
|
|
118
|
+
# Read config file (set @config_presets)
|
|
177
119
|
read_config_file
|
|
178
|
-
#
|
|
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
|
-
#
|
|
123
|
+
# Load defaults before it can be overridden
|
|
182
124
|
add_plugin_default_preset(CONF_GLOBAL_SYM)
|
|
183
|
-
#
|
|
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
|
-
#
|
|
189
|
-
|
|
190
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
226
|
-
sdk_dir = self.class.default_app_main_folder(app_name:
|
|
227
|
-
Log.log.debug{"
|
|
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{"
|
|
230
|
-
#
|
|
231
|
-
former_sdk_folder = File.join(self.class.default_app_main_folder(app_name: Info::CMD_NAME),
|
|
232
|
-
Log.log.debug{"
|
|
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{"
|
|
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
|
-
#
|
|
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
|
-
|
|
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.
|
|
255
|
-
|
|
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.
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
283
|
+
# @return only files
|
|
311
284
|
def trusted_cert_locations
|
|
312
285
|
locations = @certificate_paths
|
|
313
286
|
if locations.nil?
|
|
314
|
-
#
|
|
287
|
+
# Compute default locations
|
|
315
288
|
self.trusted_cert_locations = SpecialValues::DEF
|
|
316
289
|
locations = @certificate_paths
|
|
317
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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{"
|
|
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
|
-
#
|
|
376
|
+
# Get verification period
|
|
430
377
|
delay_days = options.get_option(:version_check_days, mandatory: true).to_i
|
|
431
|
-
#
|
|
378
|
+
# Check only if not zero day
|
|
432
379
|
return if delay_days.eql?(0)
|
|
433
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
392
|
+
# Generate timestamp
|
|
446
393
|
last_check_array[0] = current_date.strftime(GEM_CHECK_DATE_FMT)
|
|
447
394
|
check_date_persist.save
|
|
448
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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{"
|
|
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
|
-
#
|
|
498
|
-
#
|
|
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 #
|
|
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, "
|
|
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|
|
|
497
|
+
value.each{ |f| Plugins::Factory.instance.add_lookup_folder(f)}
|
|
538
498
|
end
|
|
539
499
|
|
|
540
500
|
def option_plugin_folder
|
|
541
|
-
return
|
|
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
|
-
#
|
|
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
|
-
#
|
|
524
|
+
# Files search for configuration, by default the one given by user
|
|
565
525
|
search_files = [@option_config_file]
|
|
566
|
-
#
|
|
526
|
+
# Find first existing file (or nil)
|
|
567
527
|
conf_file_to_load = search_files.find{ |f| File.exist?(f)}
|
|
568
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
624
|
+
# Collect info from ascp executable
|
|
665
625
|
data = Ascp::Installation.instance.ascp_info
|
|
666
|
-
#
|
|
626
|
+
# Add command line transfer spec
|
|
667
627
|
data['ts'] = transfer.option_transfer_spec
|
|
668
|
-
#
|
|
628
|
+
# Add keys
|
|
669
629
|
DataRepository::ELEMENTS.each_with_object(data){ |i, h| h[i.to_s] = DataRepository.instance.item(i)}
|
|
670
|
-
#
|
|
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
|
-
#
|
|
686
|
-
Products::Transferd.sdk_directory = self.class.default_app_main_folder(app_name:
|
|
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
|
-
|
|
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
|
-
#
|
|
715
|
-
Products::Transferd.sdk_directory = self.class.default_app_main_folder(app_name:
|
|
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
|
-
#
|
|
690
|
+
# Legacy actions available globally
|
|
731
691
|
PRESET_GBL_ACTIONS = %i[list overview lookup secure].freeze
|
|
732
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|
-
|
|
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 #
|
|
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 #
|
|
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 #
|
|
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 #
|
|
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, '
|
|
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
|
-
|
|
927
|
-
plugin_class =
|
|
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.
|
|
932
|
-
path:
|
|
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/
|
|
902
|
+
require 'aspera/cli/plugins/base'
|
|
942
903
|
module Aspera
|
|
943
904
|
module Cli
|
|
944
905
|
module Plugins
|
|
945
|
-
class #{plugin_name.
|
|
906
|
+
class #{plugin_name.snake_to_capital} < Base
|
|
946
907
|
ACTIONS=[]
|
|
947
|
-
def execute_action
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
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
|
-
#
|
|
920
|
+
# Interactive mode
|
|
958
921
|
options.ask_missing_mandatory = true
|
|
959
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
1051
|
-
# @param email_template_default [String] default template, can be
|
|
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
|
-
#
|
|
1035
|
+
# Create a binding with only variables defined in values
|
|
1065
1036
|
template_binding = Environment.empty_binding
|
|
1066
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
1100
|
-
#
|
|
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
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
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, "
|
|
1119
|
-
return default_config_name
|
|
1095
|
+
raise Cli::Error, "No such preset: #{default_config_name}"
|
|
1120
1096
|
end
|
|
1121
|
-
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|