aspera-cli 4.26.2 → 4.27.0
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 +281 -21
- data/CONTRIBUTING.md +98 -7
- data/bin/ascli +3 -3
- data/docs/README.md +10757 -0
- data/docs/test-mcp-with-ai.md +202 -0
- data/lib/aspera/agent/base.rb +20 -6
- data/lib/aspera/agent/connect.rb +53 -16
- data/lib/aspera/agent/desktop.rb +52 -17
- data/lib/aspera/agent/direct.rb +26 -24
- data/lib/aspera/agent/httpgw.rb +1 -1
- data/lib/aspera/agent/node.rb +32 -0
- data/lib/aspera/agent/transferd.rb +39 -3
- data/lib/aspera/api/alee.rb +1 -1
- data/lib/aspera/api/aoc.rb +14 -16
- data/lib/aspera/api/cos_node.rb +1 -1
- data/lib/aspera/api/faspex.rb +15 -14
- data/lib/aspera/api/httpgw.rb +4 -4
- data/lib/aspera/api/node.rb +26 -23
- data/lib/aspera/api/queries/bss_subscription_account.graphql +18 -0
- data/lib/aspera/api/queries/bss_subscription_usage.graphql +18 -0
- data/lib/aspera/ascmd.rb +21 -19
- data/lib/aspera/ascp/installation.rb +37 -38
- data/lib/aspera/ascp/management.rb +2 -3
- data/lib/aspera/cli/ascp_actions.rb +155 -0
- data/lib/aspera/cli/async_transfer_store.rb +81 -0
- data/lib/aspera/cli/bootstrapper.rb +197 -0
- data/lib/aspera/cli/command_registry.rb +145 -0
- data/lib/aspera/cli/command_spec.rb +138 -0
- data/lib/aspera/cli/context.rb +16 -3
- data/lib/aspera/cli/error.rb +16 -0
- data/lib/aspera/cli/extended_value.rb +2 -1
- data/lib/aspera/cli/formatter.rb +41 -27
- data/lib/aspera/cli/gem_checker.rb +65 -0
- data/lib/aspera/cli/hints.rb +2 -2
- data/lib/aspera/cli/http.rb +70 -19
- data/lib/aspera/cli/info.rb +2 -0
- data/lib/aspera/cli/mailer.rb +97 -0
- data/lib/aspera/cli/mcp_tool.rb +198 -0
- data/lib/aspera/cli/options.schema.yaml +364 -7
- data/lib/aspera/cli/{manager.rb → parser.rb} +334 -142
- data/lib/aspera/cli/plugins/alee.rb +20 -22
- data/lib/aspera/cli/plugins/aoc.rb +1166 -853
- data/lib/aspera/cli/plugins/ats.rb +200 -161
- data/lib/aspera/cli/plugins/base.rb +457 -100
- data/lib/aspera/cli/plugins/basic_auth.rb +14 -4
- data/lib/aspera/cli/plugins/config.rb +428 -754
- data/lib/aspera/cli/plugins/console.rb +106 -64
- data/lib/aspera/cli/plugins/cos.rb +44 -32
- data/lib/aspera/cli/plugins/factory.rb +4 -4
- data/lib/aspera/cli/plugins/faspex.rb +294 -257
- data/lib/aspera/cli/plugins/faspex5.rb +585 -357
- data/lib/aspera/cli/plugins/faspio.rb +49 -51
- data/lib/aspera/cli/plugins/httpgw.rb +18 -25
- data/lib/aspera/cli/plugins/mcp.rb +279 -0
- data/lib/aspera/cli/plugins/node.rb +998 -794
- data/lib/aspera/cli/plugins/oauth.rb +7 -10
- data/lib/aspera/cli/plugins/orchestrator.rb +111 -134
- data/lib/aspera/cli/plugins/preview.rb +180 -139
- data/lib/aspera/cli/plugins/server.rb +124 -79
- data/lib/aspera/cli/plugins/shares.rb +301 -107
- data/lib/aspera/cli/preset_actions.rb +139 -0
- data/lib/aspera/cli/preset_manager.rb +13 -12
- data/lib/aspera/cli/result.rb +61 -11
- data/lib/aspera/cli/runner.rb +275 -141
- data/lib/aspera/cli/secret_finder.rb +40 -0
- data/lib/aspera/cli/special_values.rb +1 -0
- data/lib/aspera/cli/sync_actions.rb +83 -59
- data/lib/aspera/cli/terminal_formatter.rb +1 -1
- data/lib/aspera/cli/transfer_actions.rb +83 -0
- data/lib/aspera/cli/transfer_agent.rb +116 -51
- data/lib/aspera/cli/transfer_progress.rb +3 -3
- data/lib/aspera/cli/vault_manager.rb +57 -0
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/cli/wizard.rb +21 -20
- data/lib/aspera/command_line_builder.rb +22 -22
- data/lib/aspera/data_repository.rb +2 -1
- data/lib/aspera/dot_container.rb +4 -4
- data/lib/aspera/environment.rb +18 -11
- data/lib/aspera/exec_spec.rb +13 -0
- data/lib/aspera/faspex_gw.rb +1 -1
- data/lib/aspera/faspex_postproc.rb +4 -3
- data/lib/aspera/graphql.rb +35 -0
- data/lib/aspera/hash_ext.rb +6 -0
- data/lib/aspera/json_rpc/client.rb +62 -0
- data/lib/aspera/json_rpc/version.rb +7 -0
- data/lib/aspera/keychain/encrypted_hash.rb +1 -1
- data/lib/aspera/keychain/factory.rb +3 -3
- data/lib/aspera/keychain/macos_security.rb +3 -3
- data/lib/aspera/link_header.rb +82 -0
- data/lib/aspera/log.rb +17 -3
- data/lib/aspera/markdown.rb +85 -2
- data/lib/aspera/node_simulator.rb +3 -1
- data/lib/aspera/oauth/base.rb +5 -5
- data/lib/aspera/oauth/boot.rb +2 -2
- data/lib/aspera/oauth/factory.rb +7 -5
- data/lib/aspera/oauth/jwt.rb +4 -4
- data/lib/aspera/oauth/web.rb +2 -2
- data/lib/aspera/persistency_action_once.rb +8 -8
- data/lib/aspera/persistency_folder.rb +10 -3
- data/lib/aspera/preview/file_types.rb +3 -3
- data/lib/aspera/preview/generator.rb +3 -3
- data/lib/aspera/preview/utils.rb +1 -1
- data/lib/aspera/products/connect.rb +1 -1
- data/lib/aspera/products/other.rb +1 -1
- data/lib/aspera/products/transferd.rb +1 -1
- data/lib/aspera/proxy_auto_config.rb +7 -6
- data/lib/aspera/rest.rb +10 -36
- data/lib/aspera/rest_error_analyzer.rb +3 -3
- data/lib/aspera/rest_errors_aspera.rb +0 -10
- data/lib/aspera/rest_list.rb +3 -3
- data/lib/aspera/schema/IBM Aspera on Cloud API-0.2.6-enhanced.yaml +1853 -510
- data/lib/aspera/schema/documentation.rb +57 -26
- data/lib/aspera/schema/reader.rb +59 -10
- data/lib/aspera/schema/registry.rb +39 -6
- data/lib/aspera/secret_hider.rb +7 -2
- data/lib/aspera/ssl.rb +4 -4
- data/lib/aspera/sync/conf.schema.yaml +2 -2
- data/lib/aspera/sync/operations.rb +10 -13
- data/lib/aspera/temp_file_manager.rb +1 -1
- data/lib/aspera/timer_limiter.rb +1 -1
- data/lib/aspera/transfer/faux_file.rb +24 -11
- data/lib/aspera/transfer/parameters.rb +23 -21
- data/lib/aspera/transfer/result.rb +74 -0
- data/lib/aspera/transfer/resumer.rb +6 -6
- data/lib/aspera/transfer/spec.rb +18 -1
- data/lib/aspera/transfer/spec.schema.yaml +12 -3
- data/lib/aspera/uri_reader.rb +2 -2
- data/lib/aspera/web_auth.rb +2 -2
- data/lib/aspera/web_server_simple.rb +8 -5
- data/lib/aspera/yaml.rb +2 -1
- data.tar.gz.sig +0 -0
- metadata +25 -3
- metadata.gz.sig +0 -0
- data/lib/aspera/json_rpc.rb +0 -52
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# cspell:ignore initdemo genkey pubkey asperasoft filelists
|
|
4
|
+
require 'aspera/cli/bootstrapper'
|
|
4
5
|
require 'aspera/cli/plugins/basic_auth'
|
|
5
6
|
require 'aspera/cli/plugins/factory'
|
|
6
7
|
require 'aspera/cli/extended_value'
|
|
@@ -8,36 +9,30 @@ require 'aspera/cli/special_values'
|
|
|
8
9
|
require 'aspera/cli/version'
|
|
9
10
|
require 'aspera/cli/formatter'
|
|
10
11
|
require 'aspera/cli/info'
|
|
11
|
-
require 'aspera/cli/transfer_progress'
|
|
12
12
|
require 'aspera/cli/wizard'
|
|
13
13
|
require 'aspera/cli/sync_actions'
|
|
14
|
-
require 'aspera/cli/
|
|
15
|
-
require 'aspera/cli/
|
|
14
|
+
require 'aspera/cli/ascp_actions'
|
|
15
|
+
require 'aspera/cli/preset_actions'
|
|
16
|
+
require 'aspera/cli/vault_manager'
|
|
17
|
+
require 'aspera/cli/transfer_actions'
|
|
18
|
+
require 'aspera/cli/gem_checker'
|
|
16
19
|
require 'aspera/ascp/installation'
|
|
17
20
|
require 'aspera/sync/operations'
|
|
18
21
|
require 'aspera/products/transferd'
|
|
19
|
-
require 'aspera/transfer/parameters'
|
|
20
22
|
require 'aspera/transfer/spec'
|
|
21
23
|
require 'aspera/schema/documentation'
|
|
22
24
|
require 'aspera/keychain/macos_security'
|
|
23
|
-
require 'aspera/proxy_auto_config'
|
|
24
25
|
require 'aspera/environment'
|
|
25
26
|
require 'aspera/persistency_action_once'
|
|
26
27
|
require 'aspera/id_generator'
|
|
27
|
-
require 'aspera/persistency_folder'
|
|
28
28
|
require 'aspera/data_repository'
|
|
29
|
-
require 'aspera/line_logger'
|
|
30
29
|
require 'aspera/rest'
|
|
31
30
|
require 'aspera/oauth/jwt'
|
|
32
31
|
require 'aspera/log'
|
|
33
32
|
require 'aspera/assert'
|
|
34
|
-
require 'aspera/oauth'
|
|
35
|
-
require 'aspera/ssl'
|
|
36
33
|
require 'openssl'
|
|
37
34
|
require 'digest'
|
|
38
35
|
require 'open3'
|
|
39
|
-
require 'date'
|
|
40
|
-
require 'erb'
|
|
41
36
|
require 'net/http'
|
|
42
37
|
|
|
43
38
|
module Aspera
|
|
@@ -46,6 +41,11 @@ module Aspera
|
|
|
46
41
|
# Manage the CLI config file
|
|
47
42
|
class Config < Base
|
|
48
43
|
include SyncActions
|
|
44
|
+
include VaultManager
|
|
45
|
+
include GemChecker
|
|
46
|
+
include AscpActions
|
|
47
|
+
include PresetActions
|
|
48
|
+
include TransferActions
|
|
49
49
|
|
|
50
50
|
class << self
|
|
51
51
|
# Folder containing plugins in the gem's main folder
|
|
@@ -53,7 +53,7 @@ module Aspera
|
|
|
53
53
|
File.dirname(File.expand_path(__FILE__))
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
-
# @return main folder where code is, i.e. .../lib
|
|
56
|
+
# @return [String] main folder where code is, i.e. .../lib
|
|
57
57
|
# Go up as many times as englobing modules (not counting class, as it is a file)
|
|
58
58
|
def gem_src_root
|
|
59
59
|
# Module.nesting[2] is Cli::Plugins
|
|
@@ -65,7 +65,7 @@ module Aspera
|
|
|
65
65
|
return Marshal.load(Marshal.dump(val))
|
|
66
66
|
end
|
|
67
67
|
|
|
68
|
-
# @return product family folder (~/.aspera)
|
|
68
|
+
# @return [String] product family folder (~/.aspera)
|
|
69
69
|
def module_family_folder
|
|
70
70
|
user_home_folder = Dir.home
|
|
71
71
|
Aspera.assert(Dir.exist?(user_home_folder), type: Cli::Error){"Home folder does not exist: #{user_home_folder}. Check your user environment."}
|
|
@@ -80,257 +80,275 @@ module Aspera
|
|
|
80
80
|
end
|
|
81
81
|
end
|
|
82
82
|
|
|
83
|
+
DEFAULT_CHECK_NEW_VERSION_DAYS = 7
|
|
84
|
+
private_constant :DEFAULT_CHECK_NEW_VERSION_DAYS
|
|
85
|
+
|
|
86
|
+
option :preset, description: 'Load the named option preset from current config file', short: 'P', handler: :option_preset
|
|
87
|
+
option :version_check_days, description: 'Period in days to check new version (zero to disable)', allowed: Allowed::TYPES_INTEGER, default: DEFAULT_CHECK_NEW_VERSION_DAYS
|
|
88
|
+
option :plugin_folder, description: 'Folder where to find additional plugins', handler: :option_plugin_folder
|
|
89
|
+
option :sdk_url, description: 'Ascp: URL to get Aspera Transfer Executables', default: SpecialValues::DEF
|
|
90
|
+
option :locations_url, description: 'Ascp: URL to get download locations of Aspera Transfer Daemon', handler: {o: Ascp::Installation.instance, m: :transferd_urls}
|
|
91
|
+
option :sdk_folder, description: 'Ascp: Path to folder with ascp (or product with "product:")', handler: {o: Products::Transferd, m: :sdk_directory}
|
|
92
|
+
option :smtp, schema: Schema::Registry::SMTP_OPTIONS
|
|
93
|
+
option :notify_to, description: 'Email: Recipient for notification of transfers'
|
|
94
|
+
option :notify_template, description: 'Email: ERB template for notification of transfers'
|
|
95
|
+
option :cache_tokens, description: 'Save and reuse OAuth tokens', allowed: Allowed::TYPES_BOOLEAN, default: true, handler: :option_cache_tokens
|
|
96
|
+
|
|
83
97
|
def initialize(**_)
|
|
84
|
-
# We need to defer parsing of options until we have the config file, so we can use @extend with @preset
|
|
85
98
|
super
|
|
86
99
|
@vault_instance = nil
|
|
87
|
-
@pac_exec = nil
|
|
88
100
|
@sdk_default_location = false
|
|
89
101
|
@option_cache_tokens = true
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
options.declare(
|
|
95
|
-
:home, 'Home folder for tool',
|
|
96
|
-
handler: {o: self, m: :main_folder},
|
|
97
|
-
default: self.class.default_app_main_folder(app_name: Info::CMD_NAME)
|
|
98
|
-
)
|
|
99
|
-
options.parse_options!
|
|
100
|
-
Log.log.debug{"#{Info::CMD_NAME} folder: #{@main_folder}"}
|
|
101
|
-
setup_persistency_and_plugin_folders
|
|
102
|
-
# Option to set config file
|
|
103
|
-
options.declare(
|
|
104
|
-
:config_file, 'Path to YAML file with preset configuration',
|
|
105
|
-
handler: {o: self, m: :option_config_file},
|
|
106
|
-
default: File.join(@main_folder, DEFAULT_CONFIG_FILENAME)
|
|
107
|
-
)
|
|
108
|
-
options.parse_options!
|
|
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
|
|
113
|
-
setup_extended_value_handlers
|
|
114
|
-
# Vault options
|
|
115
|
-
options.declare(:secret, 'Secret for access keys')
|
|
116
|
-
options.declare(:vault, 'Vault for secrets', allowed: Hash)
|
|
117
|
-
options.declare(:vault_password, 'Vault password')
|
|
118
|
-
options.parse_options!
|
|
119
|
-
# Declare generic plugin options only after handlers are declared
|
|
120
|
-
Base.declare_options(options)
|
|
121
|
-
# Configuration options
|
|
122
|
-
options.declare(:no_default, 'Do not load default configuration for plugin', allowed: Allowed::TYPES_NONE, short: 'N'){ presets.use_plugin_defaults = false }
|
|
123
|
-
options.declare(:preset, 'Load the named option preset from current config file', short: 'P', handler: {o: self, m: :option_preset})
|
|
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)
|
|
125
|
-
options.declare(:plugin_folder, 'Folder where to find additional plugins', handler: {o: self, m: :option_plugin_folder})
|
|
126
|
-
# Declare wizard options
|
|
127
|
-
@wizard = Wizard.new(self, @main_folder)
|
|
128
|
-
# Transfer SDK options
|
|
129
|
-
options.declare(:sdk_url, 'Ascp: URL to get Aspera Transfer Executables', default: SpecialValues::DEF)
|
|
102
|
+
# :no_default uses a &block callback - must stay imperative
|
|
103
|
+
options.declare(:no_default, description: 'Do not load default configuration for plugin', allowed: Allowed::TYPES_NONE, short: 'N'){presets.use_plugin_defaults = false}
|
|
104
|
+
# Declare wizard options (Wizard#initialize calls options.declare internally)
|
|
105
|
+
@wizard = Wizard.new(self, context.main_folder)
|
|
130
106
|
options.parse_options!
|
|
131
107
|
set_sdk_dir
|
|
132
|
-
options
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
# Email options
|
|
136
|
-
options.declare(:smtp, 'Email: SMTP configuration', allowed: Hash)
|
|
137
|
-
options.declare(:notify_to, 'Email: Recipient for notification of transfers')
|
|
138
|
-
options.declare(:notify_template, 'Email: ERB template for notification of transfers')
|
|
139
|
-
# HTTP options — declared by HttpConfig itself
|
|
140
|
-
context.http_config.declare_options(options)
|
|
141
|
-
options.declare(:cache_tokens, 'Save and reuse OAuth tokens', allowed: Allowed::TYPES_BOOLEAN, handler: {o: self, m: :option_cache_tokens})
|
|
142
|
-
options.declare(:fpac, 'Proxy auto configuration script')
|
|
143
|
-
options.declare(:proxy_credentials, 'HTTP proxy credentials for fpac: user, password', allowed: [Array, NilClass])
|
|
108
|
+
# HTTP options: declare metadata (class method), then bind to the instance
|
|
109
|
+
Http.declare_options(options)
|
|
110
|
+
context.http_config.bind_options(options)
|
|
144
111
|
options.parse_options!
|
|
145
|
-
@progress_bar = TransferProgress.new if options.get_option(:progress_bar)
|
|
146
|
-
setup_pac_executor
|
|
147
|
-
setup_rest_and_transfer_runtime
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
private
|
|
151
|
-
|
|
152
|
-
def setup_persistency_and_plugin_folders
|
|
153
|
-
context.persistency = PersistencyFolder.new(File.join(@main_folder, PERSISTENCY_FOLDER))
|
|
154
|
-
Plugins::Factory.instance.add_lookup_folder(self.class.gem_plugins_folder)
|
|
155
|
-
Plugins::Factory.instance.add_lookup_folder(File.join(@main_folder, ASPERA_PLUGINS_FOLDERNAME))
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
def setup_extended_value_handlers
|
|
159
|
-
ExtendedValue.instance.on(EXTEND_PRESET){ |v| presets.by_name(v)}
|
|
160
|
-
ExtendedValue.instance.on(EXTEND_VAULT){ |v| vault_value(v)}
|
|
161
|
-
ExtendedValue.instance.on(EXTEND_ARGS){ |v| options.args_as_extended(v)}
|
|
162
|
-
add_plugin_default_preset(CONF_GLOBAL_SYM)
|
|
163
112
|
end
|
|
164
113
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
114
|
+
# DSL command declarations - replaces ACTIONS + execute_action
|
|
115
|
+
command :preset, description: 'Manage configuration presets'
|
|
116
|
+
commands_under(:preset) do
|
|
117
|
+
command :list, description: 'List all presets'
|
|
118
|
+
command :overview, description: 'Display all options from all presets'
|
|
119
|
+
command :lookup, description: 'Find preset matching URL and username'
|
|
120
|
+
command :secure, description: 'Move secrets to vault',
|
|
121
|
+
arguments: [{name: :config_name, type: String, mandatory: false}]
|
|
122
|
+
command :show, description: 'Display a preset',
|
|
123
|
+
arguments: [{name: :name, type: :identifier}]
|
|
124
|
+
command :delete, description: 'Delete a preset',
|
|
125
|
+
arguments: [{name: :name, type: :identifier}]
|
|
126
|
+
command :get, description: 'Get a single parameter from a preset',
|
|
127
|
+
arguments: [{name: :name, type: :identifier}, {name: :param_name, type: String}]
|
|
128
|
+
command :unset, description: 'Remove a parameter from a preset',
|
|
129
|
+
arguments: [{name: :name, type: :identifier}, {name: :param_name, type: String}]
|
|
130
|
+
command(
|
|
131
|
+
:set, description: 'Set a parameter in a preset',
|
|
132
|
+
arguments: [{name: :name, type: :identifier}, {name: :param_name, type: String},
|
|
133
|
+
{name: :param_value, type: nil}]
|
|
134
|
+
)
|
|
135
|
+
command :initialize, description: 'Initialize a preset with a value',
|
|
136
|
+
arguments: [{name: :name, type: :identifier}, {name: :config_value, type: Hash}]
|
|
137
|
+
command :update, description: 'Update a preset with current option values',
|
|
138
|
+
arguments: [{name: :name, type: :identifier}]
|
|
139
|
+
command :ask, description: 'Interactively ask for option values',
|
|
140
|
+
arguments: [{name: :name, type: :identifier},
|
|
141
|
+
{name: :option_names, type: String, multiple: true, interactive: true}]
|
|
142
|
+
end
|
|
143
|
+
command(
|
|
144
|
+
:open, description: 'Open the configuration file in the default editor',
|
|
145
|
+
action: lambda do
|
|
146
|
+
Environment.instance.open_editor(context.presets.config_file.to_s)
|
|
147
|
+
Result::Nothing.new
|
|
175
148
|
end
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
149
|
+
)
|
|
150
|
+
command :documentation, description: 'Open the documentation in the default browser',
|
|
151
|
+
arguments: [
|
|
152
|
+
{name: :location, type: Symbol, mandatory: false, default: :github, allowed: %i[github local toc]},
|
|
153
|
+
{name: :section, type: String, mandatory: false}
|
|
154
|
+
]
|
|
155
|
+
command :genkey, description: 'Generate a new RSA private key',
|
|
156
|
+
arguments: [
|
|
157
|
+
{name: :private_key_path, type: String},
|
|
158
|
+
{name: :private_key_length, type: Integer, mandatory: false, default: OAuth::Jwt::DEFAULT_PRIV_KEY_LENGTH}
|
|
159
|
+
]
|
|
160
|
+
command :pubkey, description: 'Display the public key of an RSA private key',
|
|
161
|
+
arguments: [{name: :private_key_pem, type: String}],
|
|
162
|
+
action: ->(private_key_pem:, **){Result::Text.new(OpenSSL::PKey::RSA.new(private_key_pem).public_key.to_s)}
|
|
163
|
+
command :remote_certificate, description: 'Retrieve the certificate chain of a remote HTTPS server'
|
|
164
|
+
command :echo, description: 'Display the value of a given argument',
|
|
165
|
+
arguments: [{name: :value, type: nil}],
|
|
166
|
+
action: ->(value:, **){Result.auto(value)}
|
|
167
|
+
command :download, description: 'Download a file from a URL',
|
|
168
|
+
arguments: [
|
|
169
|
+
{name: :file_url, type: String},
|
|
170
|
+
{name: :file_dest, type: String, mandatory: false}
|
|
171
|
+
]
|
|
172
|
+
command :tokens, description: 'Manage OAuth tokens'
|
|
173
|
+
command :plugins, description: 'Manage CLI plugins'
|
|
174
|
+
command :detect, description: 'Detect the Aspera product from a URL (interactive)',
|
|
175
|
+
arguments: [{name: :url, type: String}, {name: :plugin_name, mandatory: false, default: nil}]
|
|
176
|
+
command :wizard, description: 'Run the setup wizard for an Aspera product (interactive)',
|
|
177
|
+
arguments: [{name: :url, type: String}, {name: :plugin_name, mandatory: false, default: nil},
|
|
178
|
+
{name: :preset_name, mandatory: false, default: ''}]
|
|
179
|
+
command :coffee, description: 'Display a coffee image', action: ->{Result::Image.new(COFFEE_IMAGE_URL)}
|
|
180
|
+
command :image, description: 'Display an image',
|
|
181
|
+
arguments: [{name: :image_uri, type: nil}],
|
|
182
|
+
action: ->(image_uri:, **){Result::Image.new(image_uri)}
|
|
183
|
+
command :sync, description: 'Manage Aspera Sync operations'
|
|
184
|
+
command :gem, description: 'Display gem information'
|
|
185
|
+
command :folder, description: 'Display the configuration folder path', action: ->{Result::Text.new(context.main_folder)}
|
|
186
|
+
command :file, description: 'Display the configuration file path', action: ->{Result::Text.new(context.presets.config_file)}
|
|
187
|
+
command(
|
|
188
|
+
:email_test, description: 'Send a test email',
|
|
189
|
+
action: lambda do
|
|
190
|
+
context.mailer.send_email_template(email_template_default: EMAIL_TEST_TEMPLATE)
|
|
191
|
+
Result::Nothing.new
|
|
198
192
|
end
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
public
|
|
209
|
-
|
|
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
|
-
|
|
225
|
-
def set_sdk_dir
|
|
226
|
-
# Check SDK folder is set or not, for compatibility, we check in two places
|
|
227
|
-
sdk_dir = Products::Transferd.sdk_directory rescue nil
|
|
228
|
-
if sdk_dir.nil?
|
|
229
|
-
@sdk_default_location = true
|
|
230
|
-
Log.log.debug('SDK folder is not set, checking default')
|
|
231
|
-
# New location
|
|
232
|
-
sdk_dir = self.class.default_app_main_folder(app_name: TRANSFERD_APP_NAME)
|
|
233
|
-
Log.log.debug{"Checking: #{sdk_dir}"}
|
|
234
|
-
if !Dir.exist?(sdk_dir)
|
|
235
|
-
Log.log.debug{"No such folder: #{sdk_dir}"}
|
|
236
|
-
# Former location
|
|
237
|
-
former_sdk_folder = File.join(self.class.default_app_main_folder(app_name: Info::CMD_NAME), TRANSFERD_APP_NAME)
|
|
238
|
-
Log.log.debug{"Checking: #{former_sdk_folder}"}
|
|
239
|
-
sdk_dir = former_sdk_folder if Dir.exist?(former_sdk_folder)
|
|
240
|
-
end
|
|
241
|
-
Log.log.debug{"Using: #{sdk_dir}"}
|
|
242
|
-
Products::Transferd.sdk_directory = sdk_dir
|
|
193
|
+
)
|
|
194
|
+
command :smtp_settings, description: 'Display the current SMTP settings', action: ->{Result::SingleObject.new(context.mailer.email_settings)}
|
|
195
|
+
command(
|
|
196
|
+
:proxy_check, description: 'Check the proxy returned by the PAC script for a given URL',
|
|
197
|
+
arguments: [{name: :server_url, type: String}],
|
|
198
|
+
action: lambda do |server_url:, **|
|
|
199
|
+
Aspera.assert(!context.pac_executor.nil?, type: Cli::BadArgument){'No PAC script configured, use --fpac'}
|
|
200
|
+
Result::ValueList.new(context.pac_executor.get_proxies(server_url), name: 'proxy')
|
|
243
201
|
end
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
202
|
+
)
|
|
203
|
+
command :check_update, description: 'Check if a newer version of the gem is available', action: ->{Result::SingleObject.new(check_gem_version)}
|
|
204
|
+
command :initdemo, description: 'Initialize the demo server preset'
|
|
205
|
+
command :vault, description: 'Manage secrets in the vault'
|
|
206
|
+
commands_under(:vault) do
|
|
207
|
+
command :info, description: 'Display vault information',
|
|
208
|
+
action: ->(**){Result::SingleObject.new(vault.info)}
|
|
209
|
+
command :list, description: 'List secrets in the vault',
|
|
210
|
+
action: ->(**){Result::ObjectList.new(vault.list)}
|
|
211
|
+
command :show, description: 'Show a secret by label',
|
|
212
|
+
arguments: [{name: :label, type: String}]
|
|
213
|
+
command :create, description: 'Add a new secret to the vault',
|
|
214
|
+
arguments: [{name: :info, type: Hash}]
|
|
215
|
+
command :delete, description: 'Delete a secret by label',
|
|
216
|
+
arguments: [{name: :label, type: String}]
|
|
217
|
+
command :password, description: 'Change the vault password',
|
|
218
|
+
arguments: [{name: :new_password, type: String}]
|
|
219
|
+
end
|
|
220
|
+
command :commands, description: 'List all available commands across all plugins'
|
|
221
|
+
command :options, description: 'List all options available for a plugin',
|
|
222
|
+
arguments: [{name: :plugin_name, type: String}]
|
|
223
|
+
command :test, description: 'Internal test commands'
|
|
224
|
+
command :platform, description: 'Display the current platform/architecture', action: ->{Result::Text.new(Environment.instance.architecture)}
|
|
225
|
+
command :completion, description: 'Generate shell completion scripts'
|
|
226
|
+
|
|
227
|
+
# remote_certificate sub-commands
|
|
228
|
+
commands_under(:remote_certificate) do
|
|
229
|
+
command :chain, description: 'Display the full certificate chain as PEM',
|
|
230
|
+
arguments: [{name: :remote_url, type: String}]
|
|
231
|
+
command :only, description: 'Display only the server certificate as PEM',
|
|
232
|
+
arguments: [{name: :remote_url, type: String}]
|
|
233
|
+
command :name, description: 'Display the CN of the server certificate',
|
|
234
|
+
arguments: [{name: :remote_url, type: String}]
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# tokens sub-commands
|
|
238
|
+
commands_under(:tokens) do
|
|
239
|
+
command(
|
|
240
|
+
:flush, description: 'Delete all cached OAuth tokens',
|
|
241
|
+
action: lambda do
|
|
242
|
+
require 'aspera/api/node'
|
|
243
|
+
Result::ValueList.new(OAuth::Factory.instance.flush_tokens, name: 'file')
|
|
254
244
|
end
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
245
|
+
)
|
|
246
|
+
command(
|
|
247
|
+
:list, description: 'List all cached OAuth tokens',
|
|
248
|
+
action: lambda do
|
|
249
|
+
require 'aspera/api/node'
|
|
250
|
+
Result::ObjectList.new(OAuth::Factory.instance.persisted_tokens)
|
|
259
251
|
end
|
|
260
|
-
end
|
|
261
|
-
return {
|
|
262
|
-
name: Info::GEM_NAME,
|
|
263
|
-
current: Cli::VERSION,
|
|
264
|
-
latest: latest_version,
|
|
265
|
-
need_update: Gem::Version.new(Cli::VERSION) < Gem::Version.new(latest_version)
|
|
266
|
-
}
|
|
267
|
-
end
|
|
268
|
-
|
|
269
|
-
def periodic_check_newer_gem_version
|
|
270
|
-
# Get verification period
|
|
271
|
-
delay_days = options.get_option(:version_check_days, mandatory: true).to_i
|
|
272
|
-
# Check only if not zero day
|
|
273
|
-
return if delay_days.eql?(0)
|
|
274
|
-
# Get last date from persistency
|
|
275
|
-
last_check_array = []
|
|
276
|
-
check_date_persist = PersistencyActionOnce.new(
|
|
277
|
-
manager: persistency,
|
|
278
|
-
data: last_check_array,
|
|
279
|
-
id: 'version_last_check'
|
|
280
252
|
)
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
253
|
+
command :show, description: 'Show details of a cached OAuth token',
|
|
254
|
+
arguments: [{name: :token_id, type: :identifier}]
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
# plugins sub-commands
|
|
258
|
+
commands_under(:plugins) do
|
|
259
|
+
command :list, description: 'List all available plugins'
|
|
260
|
+
command :create, description: 'Create a new plugin skeleton file',
|
|
261
|
+
arguments: [
|
|
262
|
+
{name: :name, type: String},
|
|
263
|
+
{name: :folder, type: String, mandatory: false}
|
|
264
|
+
]
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
# ascp sub-commands
|
|
268
|
+
command :ascp, description: 'Manage FASP/ascp transfer engine'
|
|
269
|
+
commands_under(:ascp) do
|
|
270
|
+
command :show, description: 'Display ascp binary path'
|
|
271
|
+
command :info, description: 'Display ascp and transfer spec information'
|
|
272
|
+
command :install, description: 'Install the transfer SDK',
|
|
273
|
+
arguments: [{name: :version, mandatory: false, default: nil}]
|
|
274
|
+
command :spec, description: 'Display the transfer spec schema'
|
|
275
|
+
command :schema, description: 'Display the transfer spec JSON schema',
|
|
276
|
+
arguments: [{name: :agent_name, mandatory: false, default: nil}]
|
|
277
|
+
command :errors, description: 'Display FASP error codes'
|
|
278
|
+
command :products, description: 'Manage installed Aspera products'
|
|
279
|
+
commands_under(%i[ascp products]) do
|
|
280
|
+
command :list, description: 'List installed Aspera products'
|
|
281
|
+
end
|
|
294
282
|
end
|
|
295
283
|
|
|
296
|
-
#
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
284
|
+
# agents sub-commands
|
|
285
|
+
command :agents, description: 'Manage transfer agents'
|
|
286
|
+
commands_under(:agents) do
|
|
287
|
+
command :list, description: 'List all transfer agents'
|
|
288
|
+
command :show, description: 'Show details for a transfer agent',
|
|
289
|
+
arguments: [{name: :agent_name, allowed: Agent::Factory::ALL.keys}]
|
|
290
|
+
command :parameters, description: 'Show configurable parameters for a transfer agent',
|
|
291
|
+
arguments: [{name: :agent_name, allowed: Agent::Factory::ALL.keys}]
|
|
301
292
|
end
|
|
302
293
|
|
|
303
|
-
#
|
|
304
|
-
|
|
305
|
-
|
|
294
|
+
# transfer async management sub-commands
|
|
295
|
+
command :transfer, description: 'Manage asynchronous transfers'
|
|
296
|
+
commands_under(:transfer) do
|
|
297
|
+
command :list, description: 'List all async transfer jobs'
|
|
298
|
+
command :status, description: 'Show status of an async transfer job',
|
|
299
|
+
arguments: [{name: :job_id, type: String}]
|
|
300
|
+
command :cleanup, description: 'Remove completed/failed/cancelled transfer entries'
|
|
306
301
|
end
|
|
307
302
|
|
|
308
|
-
|
|
309
|
-
|
|
303
|
+
# transferd sub-commands
|
|
304
|
+
command :transferd, description: 'Manage the transfer daemon (transferd)'
|
|
305
|
+
commands_under(:transferd) do
|
|
306
|
+
command :install, description: 'Install the transfer daemon'
|
|
307
|
+
command :list, description: 'List available SDK locations'
|
|
310
308
|
end
|
|
311
309
|
|
|
312
|
-
|
|
313
|
-
|
|
310
|
+
# sync sub-commands
|
|
311
|
+
commands_under(:sync) do
|
|
312
|
+
command(
|
|
313
|
+
:spec, description: 'Display the sync configuration schema',
|
|
314
|
+
action: lambda do
|
|
315
|
+
builder = Schema::Documentation.new(TerminalFormatter, Sync::Operations::CONF_SCHEMA, include_option: true).build
|
|
316
|
+
Result::ObjectList.new(builder.rows, fields: builder.columns)
|
|
317
|
+
end
|
|
318
|
+
)
|
|
319
|
+
command :admin, description: 'Manage sync database (admin operations)'
|
|
320
|
+
SyncActions.register_sync_admin_commands(self, %i[sync admin])
|
|
321
|
+
command :translate, description: 'Translate async-style arguments to sync config format',
|
|
322
|
+
arguments: [{name: :async_arguments, type: String, multiple: true}],
|
|
323
|
+
action: ->(async_arguments:, **){Result::SingleObject.new(Sync::Operations.args_to_conf(async_arguments))}
|
|
314
324
|
end
|
|
315
325
|
|
|
316
|
-
|
|
317
|
-
|
|
326
|
+
# gem sub-commands
|
|
327
|
+
commands_under(:gem) do
|
|
328
|
+
command :path, description: 'Display the gem source root path', action: ->{Result::Text.new(self.class.gem_src_root)}
|
|
329
|
+
command :version, description: 'Display the gem version', action: ->{Result::Text.new(Cli::VERSION)}
|
|
330
|
+
command :name, description: 'Display the gem name', action: ->{Result::Text.new(Info::GEM_NAME)}
|
|
318
331
|
end
|
|
319
332
|
|
|
320
|
-
|
|
321
|
-
|
|
333
|
+
# test sub-commands
|
|
334
|
+
commands_under(:test) do
|
|
335
|
+
command :throw, description: 'Raise an exception (for testing)',
|
|
336
|
+
arguments: [
|
|
337
|
+
{name: :exception_class_name, type: String},
|
|
338
|
+
{name: :exception_text, type: String}
|
|
339
|
+
]
|
|
340
|
+
command :web, description: 'Test web browser interaction', action: -> {}
|
|
322
341
|
end
|
|
323
342
|
|
|
324
|
-
|
|
325
|
-
|
|
343
|
+
# completion sub-commands
|
|
344
|
+
commands_under(:completion) do
|
|
345
|
+
command :bash, description: 'Generate bash completion script',
|
|
346
|
+
arguments: [{name: :words, type: String, multiple: true, mandatory: false}]
|
|
326
347
|
end
|
|
327
348
|
|
|
328
|
-
|
|
329
|
-
presets.lookup_preset(url: url, username: username)
|
|
330
|
-
end
|
|
349
|
+
attr_accessor :option_cache_tokens
|
|
331
350
|
|
|
332
351
|
attr_reader :gem_url
|
|
333
|
-
attr_accessor :option_config_file
|
|
334
352
|
|
|
335
353
|
def option_plugin_folder=(value)
|
|
336
354
|
value = [value] unless value.is_a?(Array)
|
|
@@ -349,554 +367,222 @@ module Aspera
|
|
|
349
367
|
when Hash
|
|
350
368
|
options.add_option_preset(value, 'set')
|
|
351
369
|
when String
|
|
352
|
-
options.add_option_preset(
|
|
370
|
+
options.add_option_preset(presets.by_name(value), 'set_by_name')
|
|
353
371
|
else
|
|
354
372
|
raise BadArgument, 'Preset definition must be a String for preset name, or Hash for set of values'
|
|
355
373
|
end
|
|
356
374
|
end
|
|
357
375
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
# Add command line transfer spec
|
|
375
|
-
data['ts'] = transfer.user_transfer_spec
|
|
376
|
-
# Add keys
|
|
377
|
-
DataRepository::ELEMENTS.each_with_object(data){ |i, h| h[i.to_s] = DataRepository.instance.item(i)}
|
|
378
|
-
# Declare those as secrets
|
|
379
|
-
SecretHider::ADDITIONAL_KEYS_TO_HIDE.concat(DataRepository::ELEMENTS.map(&:to_s))
|
|
380
|
-
return Result::SingleObject.new(data)
|
|
381
|
-
when :products
|
|
382
|
-
command = options.get_next_command(%i[list])
|
|
383
|
-
case command
|
|
384
|
-
when :list
|
|
385
|
-
return Result::ObjectList.new(Ascp::Installation.instance.installed_products, fields: %w[name app_root])
|
|
376
|
+
# DSL handlers - one method per leaf command
|
|
377
|
+
|
|
378
|
+
def action_documentation(location: :github, section: nil, **)
|
|
379
|
+
case location
|
|
380
|
+
when :github
|
|
381
|
+
section = "##{section}" unless section.nil?
|
|
382
|
+
Environment.instance.open_uri("#{Info::DOC_URL}#{section}")
|
|
383
|
+
return Result::Nothing.new
|
|
384
|
+
when :toc, :local
|
|
385
|
+
require 'aspera/markdown'
|
|
386
|
+
local_doc = File.join(self.class.gem_src_root, '..', 'docs', 'README.md')
|
|
387
|
+
Aspera.assert(File.exist?(local_doc), type: Cli::Error){"Local documentation not found: #{local_doc}"}
|
|
388
|
+
content = File.read(local_doc)
|
|
389
|
+
if location == :toc
|
|
390
|
+
entries = Markdown.toc(content)
|
|
391
|
+
return Result::ObjectList.new(entries, fields: %w[level title anchor])
|
|
386
392
|
end
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
when :schema
|
|
393
|
-
schema = Transfer::Spec::SCHEMA.current.merge({'$comment'=>'DO NOT EDIT, this file was generated from the YAML.'})
|
|
394
|
-
agent = options.get_next_argument('transfer agent name', mandatory: false)
|
|
395
|
-
schema['properties'] = schema['properties'].select{ |_k, v| CommandLineBuilder.supported_by_agent(agent, v)} unless agent.nil?
|
|
396
|
-
schema['properties'] = schema['properties'].sort.to_h
|
|
397
|
-
return Result::SingleObject.new(schema)
|
|
398
|
-
when :errors
|
|
399
|
-
error_data = []
|
|
400
|
-
Ascp::Management::ERRORS.each_pair do |code, prop|
|
|
401
|
-
error_data.push(code: code, mnemonic: prop[:c], retry: prop[:r], info: prop[:a])
|
|
393
|
+
# :local
|
|
394
|
+
if section
|
|
395
|
+
text = Markdown.extract_section(content, section)
|
|
396
|
+
Aspera.assert(!text.nil?, type: Cli::Error){"Section not found: #{section}"}
|
|
397
|
+
return Result::Text.new(text)
|
|
402
398
|
end
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
def execute_action_transferd
|
|
410
|
-
command = options.get_next_command(%i[list install])
|
|
411
|
-
case command
|
|
412
|
-
when :install
|
|
413
|
-
return install_transfer_sdk
|
|
414
|
-
when :list
|
|
415
|
-
sdk_list = Ascp::Installation.instance.sdk_locations
|
|
416
|
-
return Result::ObjectList.new(
|
|
417
|
-
sdk_list,
|
|
418
|
-
fields: sdk_list.first.keys - ['url']
|
|
419
|
-
)
|
|
420
|
-
else Aspera.error_unexpected_value(command)
|
|
399
|
+
if Environment.instance.url_method.eql?(:graphical)
|
|
400
|
+
Environment.instance.open_uri("file://#{local_doc}")
|
|
401
|
+
return Result::Nothing.new
|
|
402
|
+
end
|
|
403
|
+
return Result::Text.new(content)
|
|
421
404
|
end
|
|
422
|
-
Aspera.error_unreachable_line
|
|
423
405
|
end
|
|
424
406
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
# require id
|
|
430
|
-
PRESET_INSTANCE_ACTIONS = %i[initialize update ask set].concat(PRESET_EXIST_ACTIONS).freeze
|
|
431
|
-
PRESET_ALL_ACTIONS = (PRESET_GBL_ACTIONS + PRESET_INSTANCE_ACTIONS).freeze
|
|
407
|
+
def action_genkey(private_key_path:, private_key_length: OAuth::Jwt::DEFAULT_PRIV_KEY_LENGTH, **)
|
|
408
|
+
OAuth::Jwt.generate_rsa_private_key(path: private_key_path, length: private_key_length)
|
|
409
|
+
Result::Status.new("Generated #{private_key_length} bit RSA key: #{private_key_path}")
|
|
410
|
+
end
|
|
432
411
|
|
|
433
|
-
def
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
name = presets.global_default_preset if name.eql?(GLOBAL_DEFAULT_KEYWORD)
|
|
438
|
-
# Those operations require existing option
|
|
439
|
-
raise "no such preset: #{name}" if PRESET_EXIST_ACTIONS.include?(action) && !cp.key?(name)
|
|
440
|
-
case action
|
|
441
|
-
when :list
|
|
442
|
-
return Result::ValueList.new(cp.keys, name: 'name')
|
|
443
|
-
when :overview
|
|
444
|
-
# Display process modifies the value (hide secrets): we do not want to save removed secrets
|
|
445
|
-
data = PresetManager.deep_clone(cp)
|
|
446
|
-
formatter.hide_secrets(data)
|
|
447
|
-
result = []
|
|
448
|
-
data.each do |config, preset|
|
|
449
|
-
preset.each do |parameter, value|
|
|
450
|
-
result.push(CONF_OVERVIEW_KEYS.zip([config, parameter, value]).to_h)
|
|
451
|
-
end
|
|
452
|
-
end
|
|
453
|
-
return Result::ObjectList.new(result, fields: CONF_OVERVIEW_KEYS)
|
|
454
|
-
when :show
|
|
455
|
-
return Result::SingleObject.new(PresetManager.deep_clone(cp[name]))
|
|
456
|
-
when :delete
|
|
457
|
-
cp.delete(name)
|
|
458
|
-
return Result::Status.new("Deleted: #{name}")
|
|
459
|
-
when :get
|
|
460
|
-
param_name = options.get_next_argument('parameter name')
|
|
461
|
-
value = cp[name][param_name]
|
|
462
|
-
raise "no such option in preset #{name} : #{param_name}" if value.nil?
|
|
463
|
-
case value
|
|
464
|
-
when Numeric, String then return Result::Text.new(ExtendedValue.instance.evaluate(value.to_s, context: 'preset'))
|
|
465
|
-
end
|
|
466
|
-
return Result::SingleObject.new(value)
|
|
467
|
-
when :unset
|
|
468
|
-
param_name = options.get_next_argument('parameter name')
|
|
469
|
-
cp[name].delete(param_name)
|
|
470
|
-
return Result::Status.new("Removed: #{name}: #{param_name}")
|
|
471
|
-
when :set
|
|
472
|
-
param_name = options.get_next_argument('parameter name')
|
|
473
|
-
param_name = Manager.option_line_to_name(param_name)
|
|
474
|
-
param_value = options.get_next_argument('parameter value', validation: nil)
|
|
475
|
-
set_preset_key(name, param_name, param_value)
|
|
476
|
-
return Result::Nothing.new
|
|
477
|
-
when :initialize
|
|
478
|
-
config_value = options.get_next_argument('extended value', validation: Hash)
|
|
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}")
|
|
482
|
-
when :update
|
|
483
|
-
unprocessed_options = options.unprocessed_options_with_value
|
|
484
|
-
Log.log.debug{"opts=#{unprocessed_options}"}
|
|
485
|
-
cp[name] ||= {}
|
|
486
|
-
cp[name].merge!(unprocessed_options)
|
|
487
|
-
return Result::Status.new("Updated: #{name}")
|
|
488
|
-
when :ask
|
|
489
|
-
options.ask_missing_mandatory = true
|
|
490
|
-
cp[name] ||= {}
|
|
491
|
-
options.get_next_argument('option names', multiple: true).each do |option_name|
|
|
492
|
-
option_value = options.get_interactive(option_name, check_option: true)
|
|
493
|
-
cp[name][option_name] = option_value
|
|
494
|
-
end
|
|
495
|
-
return Result::Status.new("Updated: #{name}")
|
|
496
|
-
when :lookup
|
|
497
|
-
BasicAuth.declare_options(options)
|
|
498
|
-
url = options.get_option(:url, mandatory: true)
|
|
499
|
-
user = options.get_option(:username, mandatory: true)
|
|
500
|
-
result = lookup_preset(url: url, username: user)
|
|
501
|
-
raise Error, 'no such config found' if result.nil?
|
|
502
|
-
return Result::SingleObject.new(result)
|
|
503
|
-
when :secure
|
|
504
|
-
identifier = options.get_next_argument('config name', mandatory: false)
|
|
505
|
-
preset_names = identifier.nil? ? cp.keys : [identifier]
|
|
506
|
-
secret_keywords = %w[password secret].freeze
|
|
507
|
-
preset_names.each do |preset_name|
|
|
508
|
-
preset = cp[preset_name]
|
|
509
|
-
next unless preset.is_a?(Hash)
|
|
510
|
-
preset.each_key do |option_name|
|
|
511
|
-
secret_keywords.each do |keyword|
|
|
512
|
-
next unless option_name.end_with?(keyword)
|
|
513
|
-
vault_label = preset_name
|
|
514
|
-
incr = 0
|
|
515
|
-
until vault.get(label: vault_label, exception: false).nil?
|
|
516
|
-
vault_label = "#{preset_name}#{incr}"
|
|
517
|
-
incr += 1
|
|
518
|
-
end
|
|
519
|
-
to_set = {label: vault_label, password: preset[option_name]}
|
|
520
|
-
puts "need to encode #{preset_name}.#{option_name} -> #{vault_label} -> #{to_set}"
|
|
521
|
-
vault.set(to_set)
|
|
522
|
-
preset[option_name] = "@vault:#{vault_label}.password"
|
|
523
|
-
end
|
|
524
|
-
end
|
|
525
|
-
end
|
|
526
|
-
return Result::Status.new('Secrets secured in vault: Make sure to save the vault password securely.')
|
|
527
|
-
end
|
|
412
|
+
def action_remote_certificate_chain(remote_url:, **)
|
|
413
|
+
remote_chain = Rest.remote_certificate_chain(remote_url, as_string: false)
|
|
414
|
+
Aspera.assert(remote_chain&.first){"No certificate found for #{remote_url}"}
|
|
415
|
+
Result::Text.new(remote_chain.map(&:to_pem).join("\n"))
|
|
528
416
|
end
|
|
529
417
|
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
return Result::Text.new(OpenSSL::PKey::RSA.new(private_key_pem).public_key.to_s)
|
|
584
|
-
when :remote_certificate
|
|
585
|
-
cert_action = options.get_next_command(%i[chain only name])
|
|
586
|
-
remote_url = options.get_next_argument('remote URL')
|
|
587
|
-
remote_chain = Rest.remote_certificate_chain(remote_url, as_string: false)
|
|
588
|
-
raise "No certificate found for #{remote_url}" unless remote_chain&.first
|
|
589
|
-
case cert_action
|
|
590
|
-
when :chain
|
|
591
|
-
return Result::Text.new(remote_chain.map(&:to_pem).join("\n"))
|
|
592
|
-
when :only
|
|
593
|
-
return Result::Text.new(remote_chain.first.to_pem)
|
|
594
|
-
when :name
|
|
595
|
-
return Result::Text.new(remote_chain.first.subject.to_a.find{ |name, _, _| name == 'CN'}[1])
|
|
596
|
-
end
|
|
597
|
-
when :echo # Display the content of a value given on command line
|
|
598
|
-
return Result.auto(options.get_next_argument('value', validation: nil))
|
|
599
|
-
when :download
|
|
600
|
-
file_url = options.get_next_argument('source URL').chomp
|
|
601
|
-
file_dest = options.get_next_argument('file path', mandatory: false)
|
|
602
|
-
file_dest = File.join(transfer.destination_folder(Transfer::Spec::DIRECTION_RECEIVE), file_url.gsub(%r{.*/}, '')) if file_dest.nil?
|
|
603
|
-
Log.log.info("Downloading: #{file_url}")
|
|
604
|
-
Rest.new(base_url: file_url).call(operation: 'GET', save_to_file: file_dest)
|
|
605
|
-
return Result::Status.new("Saved to: #{file_dest}")
|
|
606
|
-
when :tokens
|
|
607
|
-
require 'aspera/api/node'
|
|
608
|
-
case options.get_next_command(%i{flush list show})
|
|
609
|
-
when :flush
|
|
610
|
-
return Result::ValueList.new(OAuth::Factory.instance.flush_tokens, name: 'file')
|
|
611
|
-
when :list
|
|
612
|
-
return Result::ObjectList.new(OAuth::Factory.instance.persisted_tokens)
|
|
613
|
-
when :show
|
|
614
|
-
data = OAuth::Factory.instance.get_token_info(options.instance_identifier)
|
|
615
|
-
raise Cli::Error, 'Unknown identifier' if data.nil?
|
|
616
|
-
return Result::SingleObject.new(data)
|
|
617
|
-
end
|
|
618
|
-
when :plugins
|
|
619
|
-
case options.get_next_command(%i[list create])
|
|
620
|
-
when :list
|
|
621
|
-
result = []
|
|
622
|
-
Plugins::Factory.instance.plugin_list.each do |name|
|
|
623
|
-
plugin_class = Plugins::Factory.instance.plugin_class(name)
|
|
624
|
-
result.push({
|
|
625
|
-
plugin: name,
|
|
626
|
-
detect: TerminalFormatter.tick(plugin_class.respond_to?(:detect)),
|
|
627
|
-
wizard: TerminalFormatter.tick(plugin_class.method_defined?(:wizard)),
|
|
628
|
-
path: Plugins::Factory.instance.plugin_source(name)
|
|
629
|
-
})
|
|
630
|
-
end
|
|
631
|
-
return Result::ObjectList.new(result, fields: %w[plugin detect wizard path])
|
|
632
|
-
when :create
|
|
633
|
-
plugin_name = options.get_next_argument('name').downcase
|
|
634
|
-
destination_folder = options.get_next_argument('folder', mandatory: false) || File.join(@main_folder, ASPERA_PLUGINS_FOLDERNAME)
|
|
635
|
-
plugin_file = File.join(destination_folder, "#{plugin_name}.rb")
|
|
636
|
-
content = <<~END_OF_PLUGIN_CODE
|
|
637
|
-
require 'aspera/cli/plugins/base'
|
|
638
|
-
module Aspera
|
|
639
|
-
module Cli
|
|
640
|
-
module Plugins
|
|
641
|
-
class #{plugin_name.snake_to_capital} < Base
|
|
642
|
-
ACTIONS=[]
|
|
643
|
-
def execute_action
|
|
644
|
-
return Result::Status.new('You called plugin #{plugin_name}')
|
|
645
|
-
end
|
|
646
|
-
end
|
|
418
|
+
def action_remote_certificate_only(remote_url:, **)
|
|
419
|
+
remote_chain = Rest.remote_certificate_chain(remote_url, as_string: false)
|
|
420
|
+
Aspera.assert(remote_chain&.first){"No certificate found for #{remote_url}"}
|
|
421
|
+
Result::Text.new(remote_chain.first.to_pem)
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
def action_remote_certificate_name(remote_url:, **)
|
|
425
|
+
remote_chain = Rest.remote_certificate_chain(remote_url, as_string: false)
|
|
426
|
+
Aspera.assert(remote_chain&.first){"No certificate found for #{remote_url}"}
|
|
427
|
+
Result::Text.new(remote_chain.first.subject.to_a.find{ |name, _, _| name == 'CN'}[1])
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
def action_download(file_url:, file_dest: nil, **)
|
|
431
|
+
file_url = file_url.chomp
|
|
432
|
+
file_dest = File.join(transfer.destination_folder(Transfer::Spec::DIRECTION_RECEIVE), file_url.gsub(%r{.*/}, '')) if file_dest.nil?
|
|
433
|
+
Log.log.info("Downloading: #{file_url}")
|
|
434
|
+
Rest.new(base_url: file_url).call(operation: 'GET', save_to_file: file_dest)
|
|
435
|
+
Result::Status.new("Saved to: #{file_dest}")
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
def action_tokens_show(token_id:, **)
|
|
439
|
+
require 'aspera/api/node'
|
|
440
|
+
data = OAuth::Factory.instance.get_token_info(token_id)
|
|
441
|
+
Aspera.assert(!data.nil?, type: Cli::Error){'Unknown identifier'}
|
|
442
|
+
Result::SingleObject.new(data)
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
def action_plugins_list
|
|
446
|
+
result = Plugins::Factory.instance.plugin_list.map do |name|
|
|
447
|
+
plugin_class = Plugins::Factory.instance.plugin_class(name)
|
|
448
|
+
{
|
|
449
|
+
plugin: name,
|
|
450
|
+
detect: TerminalFormatter.tick(plugin_class.respond_to?(:detect)),
|
|
451
|
+
wizard: TerminalFormatter.tick(plugin_class.method_defined?(:wizard)),
|
|
452
|
+
path: Plugins::Factory.instance.plugin_source(name)
|
|
453
|
+
}
|
|
454
|
+
end
|
|
455
|
+
Result::ObjectList.new(result, fields: %w[plugin detect wizard path])
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
def action_plugins_create(name:, folder: nil, **)
|
|
459
|
+
plugin_name = name.downcase
|
|
460
|
+
destination_folder = folder || File.join(context.main_folder, ASPERA_PLUGINS_FOLDERNAME)
|
|
461
|
+
plugin_file = File.join(destination_folder, "#{plugin_name}.rb")
|
|
462
|
+
content = <<~END_OF_PLUGIN_CODE
|
|
463
|
+
require 'aspera/cli/plugins/base'
|
|
464
|
+
module Aspera
|
|
465
|
+
module Cli
|
|
466
|
+
module Plugins
|
|
467
|
+
class #{plugin_name.snake_to_capital} < Base
|
|
468
|
+
command :example, description: 'example command', action: :action_example
|
|
469
|
+
def action_example
|
|
470
|
+
Result::Status.new('You called plugin #{plugin_name}')
|
|
647
471
|
end
|
|
648
472
|
end
|
|
649
473
|
end
|
|
650
|
-
|
|
651
|
-
File.write(plugin_file, content)
|
|
652
|
-
return Result::Status.new("Created #{plugin_file}")
|
|
653
|
-
end
|
|
654
|
-
when :detect, :wizard
|
|
655
|
-
# Interactive mode
|
|
656
|
-
options.ask_missing_mandatory = true
|
|
657
|
-
# Detect plugins by url and optional query
|
|
658
|
-
apps = @wizard.identify_plugins_for_url.freeze
|
|
659
|
-
return Result::ObjectList.new(apps) if action.eql?(:detect)
|
|
660
|
-
return @wizard.find(apps)
|
|
661
|
-
when :coffee
|
|
662
|
-
return Result::Image.new(COFFEE_IMAGE_URL)
|
|
663
|
-
when :image
|
|
664
|
-
return Result::Image.new(options.get_next_argument('image URI or blob'))
|
|
665
|
-
when :ascp
|
|
666
|
-
execute_action_ascp
|
|
667
|
-
when :sync
|
|
668
|
-
SyncActions.declare_options(options)
|
|
669
|
-
case options.get_next_command(%i[spec admin translate])
|
|
670
|
-
when :spec
|
|
671
|
-
builder = Schema::Documentation.new(TerminalFormatter, Sync::Operations::CONF_SCHEMA, include_option: true).build
|
|
672
|
-
return Result::ObjectList.new(builder.rows, fields: builder.columns)
|
|
673
|
-
when :admin
|
|
674
|
-
return execute_sync_admin
|
|
675
|
-
when :translate
|
|
676
|
-
return Result::SingleObject.new(Sync::Operations.args_to_conf(options.get_next_argument('async arguments', multiple: true)))
|
|
677
|
-
else Aspera.error_unreachable_line
|
|
678
|
-
end
|
|
679
|
-
when :transferd
|
|
680
|
-
execute_action_transferd
|
|
681
|
-
when :gem
|
|
682
|
-
case options.get_next_command(%i[path version name])
|
|
683
|
-
when :path then return Result::Text.new(self.class.gem_src_root)
|
|
684
|
-
when :version then return Result::Text.new(Cli::VERSION)
|
|
685
|
-
when :name then return Result::Text.new(Info::GEM_NAME)
|
|
686
|
-
else Aspera.error_unreachable_line
|
|
687
|
-
end
|
|
688
|
-
when :folder
|
|
689
|
-
return Result::Text.new(@main_folder)
|
|
690
|
-
when :file
|
|
691
|
-
return Result::Text.new(@option_config_file)
|
|
692
|
-
when :email_test
|
|
693
|
-
send_email_template(email_template_default: EMAIL_TEST_TEMPLATE)
|
|
694
|
-
return Result::Nothing.new
|
|
695
|
-
when :smtp_settings
|
|
696
|
-
return Result::SingleObject.new(email_settings)
|
|
697
|
-
when :proxy_check
|
|
698
|
-
# Ensure fpac was provided
|
|
699
|
-
options.get_option(:fpac, mandatory: true)
|
|
700
|
-
server_url = options.get_next_argument('server url')
|
|
701
|
-
return Result::ValueList.new(@pac_exec.get_proxies(server_url), name: 'proxy')
|
|
702
|
-
when :check_update
|
|
703
|
-
return Result::SingleObject.new(check_gem_version)
|
|
704
|
-
when :initdemo
|
|
705
|
-
cp = presets.config_presets
|
|
706
|
-
if cp.key?(DEMO_PRESET)
|
|
707
|
-
Log.log.warn{"Demo server preset already present: #{DEMO_PRESET}"}
|
|
708
|
-
else
|
|
709
|
-
Log.log.info{"Creating Demo server preset: #{DEMO_PRESET}"}
|
|
710
|
-
cp[DEMO_PRESET] = {
|
|
711
|
-
'url' => "ssh://#{DEMO_SERVER}.asperasoft.com:33001",
|
|
712
|
-
'username' => ASPERA,
|
|
713
|
-
'ssAP'.downcase.reverse + 'drow'.reverse => DEMO_SERVER + ASPERA # cspell:disable-line
|
|
714
|
-
}
|
|
715
|
-
end
|
|
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]}"}
|
|
719
|
-
Log.log.warn{"Use #{DEMO_PRESET} for demo: -P#{DEMO_PRESET}"} unless
|
|
720
|
-
DEMO_PRESET.eql?(cp[PresetManager::Key::DEFAULTS][SERVER_COMMAND])
|
|
721
|
-
else
|
|
722
|
-
cp[PresetManager::Key::DEFAULTS][SERVER_COMMAND] = DEMO_PRESET
|
|
723
|
-
Log.log.info{"Setting server default preset to : #{DEMO_PRESET}"}
|
|
474
|
+
end
|
|
724
475
|
end
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
when :platform
|
|
729
|
-
return Result::Text.new(Environment.instance.architecture)
|
|
730
|
-
when :completion
|
|
731
|
-
return execute_completion
|
|
732
|
-
else Aspera.error_unreachable_line
|
|
733
|
-
end
|
|
476
|
+
END_OF_PLUGIN_CODE
|
|
477
|
+
File.write(plugin_file, content)
|
|
478
|
+
Result::Status.new("Created #{plugin_file}")
|
|
734
479
|
end
|
|
735
480
|
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
end
|
|
747
|
-
Process.exit(0)
|
|
748
|
-
else Aspera.error_unreachable_line
|
|
749
|
-
end
|
|
481
|
+
def action_detect(url:, plugin_name: nil, **)
|
|
482
|
+
options.ask_missing_mandatory = true
|
|
483
|
+
apps = @wizard.identify_plugins_for_url(url: url, plugin_name: plugin_name).freeze
|
|
484
|
+
Result::ObjectList.new(apps)
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
def action_wizard(url:, plugin_name: nil, preset_name: '', **)
|
|
488
|
+
options.ask_missing_mandatory = true
|
|
489
|
+
apps = @wizard.identify_plugins_for_url(url: url, plugin_name: plugin_name).freeze
|
|
490
|
+
@wizard.find(apps, preset_name: preset_name)
|
|
750
491
|
end
|
|
751
492
|
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
smtp = smtp.symbolize_keys
|
|
757
|
-
unsupported = smtp.keys - SMTP_CONF_PARAMS
|
|
758
|
-
raise Cli::Error, "Unsupported SMTP parameter: #{unsupported.join(', ')}, use: #{SMTP_CONF_PARAMS.join(', ')}" unless unsupported.empty?
|
|
759
|
-
# Defaults
|
|
760
|
-
# smtp[:ssl] = nil (false)
|
|
761
|
-
smtp[:tls] = !smtp[:ssl] unless smtp.key?(:tls)
|
|
762
|
-
smtp[:port] ||= if smtp[:tls]
|
|
763
|
-
587
|
|
764
|
-
elsif smtp[:ssl]
|
|
765
|
-
465
|
|
493
|
+
def action_initdemo
|
|
494
|
+
cp = presets.config_presets
|
|
495
|
+
if cp.key?(DEMO_PRESET)
|
|
496
|
+
Log.log.warn{"Demo server preset already present: #{DEMO_PRESET}"}
|
|
766
497
|
else
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
%i[server port domain].each do |n|
|
|
774
|
-
Aspera.assert(smtp.key?(n)){"Missing mandatory smtp parameter: #{n}"}
|
|
498
|
+
Log.log.info{"Creating Demo server preset: #{DEMO_PRESET}"}
|
|
499
|
+
cp[DEMO_PRESET] = {
|
|
500
|
+
'url' => "ssh://#{DEMO_SERVER}.asperasoft.com:33001",
|
|
501
|
+
'username' => ASPERA,
|
|
502
|
+
'ssAP'.downcase.reverse + 'drow'.reverse => DEMO_SERVER + ASPERA # cspell:disable-line
|
|
503
|
+
}
|
|
775
504
|
end
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
values[:to] ||= options.get_option(:notify_to, mandatory: true)
|
|
785
|
-
notify_template = options.get_option(:notify_template, mandatory: email_template_default.nil?) || email_template_default
|
|
786
|
-
mail_conf = email_settings
|
|
787
|
-
values[:from_name] ||= mail_conf[:from_name]
|
|
788
|
-
values[:from_email] ||= mail_conf[:from_email]
|
|
789
|
-
%i[to from_email].each do |n|
|
|
790
|
-
Aspera.assert_type(values[n], String){"Missing email parameter: #{n} in config"}
|
|
791
|
-
end
|
|
792
|
-
start_options = [mail_conf[:domain]]
|
|
793
|
-
start_options.push(mail_conf[:username], mail_conf[:password], :login) if mail_conf.key?(:username) && mail_conf.key?(:password)
|
|
794
|
-
# Create a binding with only variables defined in values
|
|
795
|
-
template_binding = Environment.empty_binding
|
|
796
|
-
# Add variables to binding
|
|
797
|
-
values.each do |k, v|
|
|
798
|
-
Aspera.assert_type(k, Symbol)
|
|
799
|
-
template_binding.local_variable_set(k, v)
|
|
505
|
+
cp[PresetManager::Key::DEFAULTS] ||= {}
|
|
506
|
+
if cp[PresetManager::Key::DEFAULTS].key?(SERVER_COMMAND)
|
|
507
|
+
Log.log.warn{"Server default preset already set to: #{cp[PresetManager::Key::DEFAULTS][SERVER_COMMAND]}"}
|
|
508
|
+
Log.log.warn{"Use #{DEMO_PRESET} for demo: -P#{DEMO_PRESET}"} unless
|
|
509
|
+
DEMO_PRESET.eql?(cp[PresetManager::Key::DEFAULTS][SERVER_COMMAND])
|
|
510
|
+
else
|
|
511
|
+
cp[PresetManager::Key::DEFAULTS][SERVER_COMMAND] = DEMO_PRESET
|
|
512
|
+
Log.log.info{"Setting server default preset to : #{DEMO_PRESET}"}
|
|
800
513
|
end
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
514
|
+
Result::Status.new('Done')
|
|
515
|
+
end
|
|
516
|
+
|
|
517
|
+
def action_commands
|
|
518
|
+
commands = Plugins::Factory.instance.plugin_list.flat_map do |name|
|
|
519
|
+
plugin_class = Plugins::Factory.instance.plugin_class(name)
|
|
520
|
+
reg = plugin_class.command_registry
|
|
521
|
+
reg.all_paths.reject{ |path| reg.children_of(path).any?}.map do |path|
|
|
522
|
+
spec = reg[path]
|
|
523
|
+
arg_tokens = spec&.arguments.to_a.map do |a|
|
|
524
|
+
token =
|
|
525
|
+
if a.allowed
|
|
526
|
+
a.allowed.join('|')
|
|
527
|
+
else
|
|
528
|
+
a.name.to_s
|
|
529
|
+
end
|
|
530
|
+
token += '...' if a.multiple
|
|
531
|
+
a.mandatory ? "<#{token}>" : "[<#{token}>]"
|
|
532
|
+
end
|
|
533
|
+
syntax = ([name.to_s] + path.map(&:to_s) + arg_tokens).join(' ')
|
|
534
|
+
{
|
|
535
|
+
syntax: syntax,
|
|
536
|
+
description: spec&.description.to_s
|
|
537
|
+
}
|
|
538
|
+
end
|
|
810
539
|
end
|
|
811
|
-
|
|
812
|
-
end
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
when :create
|
|
826
|
-
vault.set(options.get_next_argument('info', validation: Hash).symbolize_keys)
|
|
827
|
-
return Result::Status.new('Secret added')
|
|
828
|
-
when :delete
|
|
829
|
-
label_to_delete = options.get_next_argument('label')
|
|
830
|
-
vault.delete(label: label_to_delete)
|
|
831
|
-
return Result::Status.new("Secret deleted: #{label_to_delete}")
|
|
832
|
-
when :password
|
|
833
|
-
Aspera.assert(vault.respond_to?(:change_password), 'Vault does not support password change')
|
|
834
|
-
vault.change_password(options.get_next_argument('new_password'))
|
|
835
|
-
return Result::Status.new('Vault password updated')
|
|
540
|
+
Result::ObjectList.new(commands, fields: %w[syntax description])
|
|
541
|
+
end
|
|
542
|
+
|
|
543
|
+
def action_options(plugin_name:, **)
|
|
544
|
+
# Instantiate the plugin so that it registers all its options in context.options
|
|
545
|
+
Plugins::Factory.instance.create(plugin_name.to_sym, context: context)
|
|
546
|
+
rows = context.options.declared_options.map do |sym, opt|
|
|
547
|
+
row = {
|
|
548
|
+
option: "--#{sym.to_s.tr('_', '-')}",
|
|
549
|
+
description: opt.description.to_s
|
|
550
|
+
}
|
|
551
|
+
row[:allowed] = opt.values.join('|') if opt.values&.any?
|
|
552
|
+
row[:deprecated] = opt.deprecation if opt.deprecation
|
|
553
|
+
row
|
|
836
554
|
end
|
|
555
|
+
Result::ObjectList.new(rows, fields: %w[option description allowed deprecated])
|
|
837
556
|
end
|
|
838
557
|
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
raise
|
|
843
|
-
# This raise exception if label not found:
|
|
844
|
-
info = vault.get(label: m[0])
|
|
845
|
-
value = info[m[1].to_sym]
|
|
846
|
-
raise "no such entry value: #{m[1]}" if value.nil?
|
|
847
|
-
return value
|
|
848
|
-
end
|
|
849
|
-
|
|
850
|
-
# @return [Object] vault, from options or cache
|
|
851
|
-
def vault
|
|
852
|
-
return @vault_instance unless @vault_instance.nil?
|
|
853
|
-
info = options.get_option(:vault).symbolize_keys
|
|
854
|
-
info[:type] ||= 'file'
|
|
855
|
-
require 'aspera/keychain/factory'
|
|
856
|
-
@vault_instance = Keychain::Factory.create(
|
|
857
|
-
info,
|
|
858
|
-
Info::CMD_NAME,
|
|
859
|
-
@main_folder,
|
|
860
|
-
options.get_option(:vault_password)
|
|
861
|
-
)
|
|
558
|
+
def action_test_throw(exception_class_name:, exception_text:, **)
|
|
559
|
+
type = Object.const_get(exception_class_name)
|
|
560
|
+
Aspera.assert(type <= Exception){"#{type} is not an exception: #{type.class}"}
|
|
561
|
+
raise type, exception_text
|
|
862
562
|
end
|
|
863
563
|
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
raise type, exception_text
|
|
875
|
-
when :web
|
|
876
|
-
end
|
|
877
|
-
end
|
|
878
|
-
|
|
879
|
-
# Lookup the corresponding secret for the given URL and usernames
|
|
880
|
-
# @param url [String] Server URL
|
|
881
|
-
# @param username [String] Username
|
|
882
|
-
# @return [String, nil] Secret if found
|
|
883
|
-
def lookup_secret(url:, username:)
|
|
884
|
-
secret = options.get_option(:secret)
|
|
885
|
-
if secret.eql?('PRESET')
|
|
886
|
-
conf = presets.lookup_preset(url: url, username: username)
|
|
887
|
-
if conf.is_a?(Hash)
|
|
888
|
-
Log.log.debug{"Found preset #{conf} with URL and username"}
|
|
889
|
-
secret = conf['password']
|
|
564
|
+
def action_completion_bash(words: nil, **)
|
|
565
|
+
if words.nil? || words.empty?
|
|
566
|
+
# Level 0: propose plugin names
|
|
567
|
+
Plugins::Factory.instance.plugin_list.each{ |p| puts p}
|
|
568
|
+
else
|
|
569
|
+
plugin_sym = words.first.to_sym
|
|
570
|
+
plugin_class = begin
|
|
571
|
+
Plugins::Factory.instance.plugin_class(plugin_sym)
|
|
572
|
+
rescue StandardError
|
|
573
|
+
Process.exit(0)
|
|
890
574
|
end
|
|
575
|
+
# Navigate into the registry using remaining words as path
|
|
576
|
+
path = words[1..].map(&:to_sym)
|
|
577
|
+
plugin_class.command_registry.children_of(path).each_key{ |k| puts k}
|
|
891
578
|
end
|
|
892
|
-
|
|
579
|
+
Process.exit(0)
|
|
893
580
|
end
|
|
581
|
+
|
|
894
582
|
# Folder in $HOME for application files (~/.aspera)
|
|
895
583
|
ASPERA_HOME_FOLDER_NAME = '.aspera'
|
|
896
584
|
# Default config file name
|
|
897
585
|
DEFAULT_CONFIG_FILENAME = 'config.yaml'
|
|
898
|
-
# Used in execute_preset / execute_action to identify the global default keyword
|
|
899
|
-
GLOBAL_DEFAULT_KEYWORD = 'GLOBAL'
|
|
900
586
|
CONF_GLOBAL_SYM = :config
|
|
901
587
|
# Folder containing custom plugins in user's config folder
|
|
902
588
|
ASPERA_PLUGINS_FOLDERNAME = 'plugins'
|
|
@@ -905,7 +591,6 @@ module Aspera
|
|
|
905
591
|
REST_EXCEPTIONS_LOG_FILENAME = 'rest_exceptions.log'
|
|
906
592
|
ASPERA = 'aspera'
|
|
907
593
|
SERVER_COMMAND = 'server'
|
|
908
|
-
TRANSFERD_APP_NAME = 'sdk'
|
|
909
594
|
DEMO_SERVER = 'demo'
|
|
910
595
|
DEMO_PRESET = 'demoserver' # cspell: disable-line
|
|
911
596
|
EMAIL_TEST_TEMPLATE = <<~END_OF_TEMPLATE
|
|
@@ -918,15 +603,9 @@ module Aspera
|
|
|
918
603
|
# Special extended values
|
|
919
604
|
EXTEND_PRESET = :preset
|
|
920
605
|
EXTEND_VAULT = :vault
|
|
921
|
-
EXTEND_ARGS = :''
|
|
922
|
-
DEFAULT_CHECK_NEW_VERSION_DAYS = 7
|
|
923
606
|
COFFEE_IMAGE_URL = 'https://enjoyjava.com/wp-content/uploads/2018/01/How-to-make-strong-coffee.jpg'
|
|
924
|
-
GEM_CHECK_DATE_FMT = '%Y/%m/%d'
|
|
925
|
-
CONF_OVERVIEW_KEYS = %w[preset parameter value].freeze
|
|
926
|
-
SMTP_CONF_PARAMS = %i[server tls ssl port domain username password from_name from_email].freeze
|
|
927
607
|
private_constant :ASPERA_HOME_FOLDER_NAME,
|
|
928
608
|
:DEFAULT_CONFIG_FILENAME,
|
|
929
|
-
:GLOBAL_DEFAULT_KEYWORD,
|
|
930
609
|
:CONF_GLOBAL_SYM,
|
|
931
610
|
:ASPERA_PLUGINS_FOLDERNAME,
|
|
932
611
|
:PERSISTENCY_FOLDER,
|
|
@@ -934,17 +613,12 @@ module Aspera
|
|
|
934
613
|
:REST_EXCEPTIONS_LOG_FILENAME,
|
|
935
614
|
:ASPERA,
|
|
936
615
|
:SERVER_COMMAND,
|
|
937
|
-
:TRANSFERD_APP_NAME,
|
|
938
616
|
:DEMO_SERVER,
|
|
939
617
|
:DEMO_PRESET,
|
|
940
618
|
:EMAIL_TEST_TEMPLATE,
|
|
941
619
|
:EXTEND_PRESET,
|
|
942
620
|
:EXTEND_VAULT,
|
|
943
|
-
:
|
|
944
|
-
:COFFEE_IMAGE_URL,
|
|
945
|
-
:GEM_CHECK_DATE_FMT,
|
|
946
|
-
:CONF_OVERVIEW_KEYS,
|
|
947
|
-
:SMTP_CONF_PARAMS
|
|
621
|
+
:COFFEE_IMAGE_URL
|
|
948
622
|
end
|
|
949
623
|
end
|
|
950
624
|
end
|