aspera-cli 4.26.1 → 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 +300 -22
- data/CONTRIBUTING.md +100 -12
- data/bin/ascli +5 -5
- 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 +27 -25
- data/lib/aspera/agent/factory.rb +4 -0
- data/lib/aspera/agent/httpgw.rb +2 -2
- data/lib/aspera/agent/node.rb +34 -2
- data/lib/aspera/agent/transferd.rb +39 -3
- data/lib/aspera/api/alee.rb +1 -1
- data/lib/aspera/api/aoc.rb +56 -51
- data/lib/aspera/api/cos_node.rb +2 -2
- data/lib/aspera/api/faspex.rb +22 -20
- data/lib/aspera/api/httpgw.rb +4 -4
- data/lib/aspera/api/node.rb +33 -30
- 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 +25 -23
- data/lib/aspera/ascp/installation.rb +43 -40
- 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 +71 -0
- data/lib/aspera/cli/error.rb +16 -0
- data/lib/aspera/cli/extended_value.rb +5 -3
- data/lib/aspera/cli/formatter.rb +122 -209
- data/lib/aspera/cli/gem_checker.rb +65 -0
- data/lib/aspera/cli/hints.rb +2 -2
- data/lib/aspera/cli/http.rb +218 -0
- 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} +337 -144
- data/lib/aspera/cli/plugins/alee.rb +20 -22
- data/lib/aspera/cli/plugins/aoc.rb +1173 -849
- data/lib/aspera/cli/plugins/ats.rb +200 -161
- data/lib/aspera/cli/plugins/base.rb +470 -107
- data/lib/aspera/cli/plugins/basic_auth.rb +14 -4
- data/lib/aspera/cli/plugins/config.rb +434 -1032
- 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 +7 -4
- data/lib/aspera/cli/plugins/faspex.rb +296 -259
- data/lib/aspera/cli/plugins/faspex5.rb +592 -344
- 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 +1001 -797
- data/lib/aspera/cli/plugins/oauth.rb +7 -10
- data/lib/aspera/cli/plugins/orchestrator.rb +111 -139
- data/lib/aspera/cli/plugins/preview.rb +183 -142
- data/lib/aspera/cli/plugins/server.rb +125 -80
- 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 +236 -0
- data/lib/aspera/cli/result.rb +360 -0
- data/lib/aspera/cli/runner.rb +473 -0
- 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 +84 -56
- data/lib/aspera/cli/terminal_formatter.rb +65 -0
- 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 +9 -9
- data/lib/aspera/cli/vault_manager.rb +57 -0
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/cli/wizard.rb +26 -26
- data/lib/aspera/command_line_builder.rb +23 -23
- data/lib/aspera/data_repository.rb +6 -1
- data/lib/aspera/dot_container.rb +5 -5
- data/lib/aspera/environment.rb +39 -13
- data/lib/aspera/exec_spec.rb +13 -0
- data/lib/aspera/faspex_gw.rb +2 -2
- 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/base.rb +1 -1
- data/lib/aspera/keychain/encrypted_hash.rb +2 -2
- data/lib/aspera/keychain/factory.rb +4 -4
- data/lib/aspera/keychain/macos_security.rb +4 -4
- data/lib/aspera/link_header.rb +82 -0
- data/lib/aspera/log.rb +23 -5
- data/lib/aspera/markdown.rb +85 -2
- data/lib/aspera/nagios.rb +2 -2
- data/lib/aspera/node_simulator.rb +3 -1
- data/lib/aspera/oauth/base.rb +10 -10
- data/lib/aspera/oauth/boot.rb +43 -0
- data/lib/aspera/oauth/factory.rb +41 -15
- data/lib/aspera/oauth/jwt.rb +4 -4
- data/lib/aspera/oauth/web.rb +4 -4
- data/lib/aspera/oauth.rb +1 -0
- data/lib/aspera/persistency_action_once.rb +10 -10
- data/lib/aspera/persistency_folder.rb +10 -3
- data/lib/aspera/preview/file_types.rb +7 -3
- data/lib/aspera/preview/generator.rb +3 -3
- data/lib/aspera/preview/utils.rb +1 -1
- data/lib/aspera/products/connect.rb +4 -1
- data/lib/aspera/products/other.rb +1 -1
- data/lib/aspera/products/transferd.rb +3 -3
- data/lib/aspera/proxy_auto_config.rb +10 -9
- data/lib/aspera/rest.rb +18 -40
- data/lib/aspera/rest_error_analyzer.rb +7 -3
- data/lib/aspera/rest_errors_aspera.rb +0 -10
- data/lib/aspera/rest_list.rb +13 -6
- data/lib/aspera/schema/IBM Aspera Faspex API-5.0-enhanced.yaml +14 -4
- data/lib/aspera/schema/IBM Aspera on Cloud API-0.2.6-enhanced.yaml +2730 -648
- data/lib/aspera/schema/async_tables.yaml +361 -0
- data/lib/aspera/schema/documentation.rb +57 -26
- data/lib/aspera/schema/reader.rb +60 -11
- data/lib/aspera/schema/registry.rb +50 -10
- data/lib/aspera/secret_hider.rb +11 -2
- data/lib/aspera/ssl.rb +4 -4
- data/lib/aspera/sync/conf.schema.yaml +2 -2
- data/lib/aspera/sync/database.rb +14 -10
- data/lib/aspera/sync/operations.rb +15 -18
- data/lib/aspera/temp_file_manager.rb +5 -1
- data/lib/aspera/timer_limiter.rb +1 -1
- data/lib/aspera/transfer/faux_file.rb +24 -11
- data/lib/aspera/transfer/parameters.rb +27 -25
- data/lib/aspera/transfer/result.rb +74 -0
- data/lib/aspera/transfer/resumer.rb +7 -7
- data/lib/aspera/transfer/spec.rb +18 -1
- data/lib/aspera/transfer/spec.schema.yaml +26 -8
- data/lib/aspera/transfer/uri.rb +1 -1
- 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 +45 -16
- metadata.gz.sig +0 -0
- data/lib/aspera/cli/main.rb +0 -480
- data/lib/aspera/json_rpc.rb +0 -51
data/lib/aspera/cli/main.rb
DELETED
|
@@ -1,480 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'aspera/cli/manager'
|
|
4
|
-
require 'aspera/cli/formatter'
|
|
5
|
-
require 'aspera/cli/plugins/config'
|
|
6
|
-
require 'aspera/cli/extended_value'
|
|
7
|
-
require 'aspera/cli/plugins/factory'
|
|
8
|
-
require 'aspera/cli/transfer_agent'
|
|
9
|
-
require 'aspera/cli/version'
|
|
10
|
-
require 'aspera/cli/info'
|
|
11
|
-
require 'aspera/cli/hints'
|
|
12
|
-
require 'aspera/secret_hider'
|
|
13
|
-
require 'aspera/log'
|
|
14
|
-
require 'aspera/assert'
|
|
15
|
-
require 'aspera/schema/documentation'
|
|
16
|
-
require 'aspera/schema/registry'
|
|
17
|
-
require 'net/ssh/errors'
|
|
18
|
-
require 'openssl'
|
|
19
|
-
|
|
20
|
-
module Aspera
|
|
21
|
-
module Cli
|
|
22
|
-
# Global objects shared with plugins
|
|
23
|
-
class Context
|
|
24
|
-
# @type [Array<Symbol>]
|
|
25
|
-
MEMBERS = %i[options transfer config formatter persistency man_header].freeze
|
|
26
|
-
# @!attribute [rw] options
|
|
27
|
-
# @return [Manager] the command line options manager
|
|
28
|
-
# @!attribute [rw] transfer
|
|
29
|
-
# @return [TransferAgent] the transfer agent, used by transfer plugins
|
|
30
|
-
# @!attribute [rw] config
|
|
31
|
-
# @return [Plugins::Config] the configuration plugin, used by plugins to get configuration values and presets
|
|
32
|
-
# @!attribute [rw] formatter
|
|
33
|
-
# @return [Formatter] the formatter, used by plugins to display results and messages
|
|
34
|
-
# @!attribute [rw] persistency
|
|
35
|
-
# @return [Object] # whatever the type is
|
|
36
|
-
# @!attribute [rw] man_header
|
|
37
|
-
# @return [Boolean] whether to display the manual header in plugin help
|
|
38
|
-
attr_accessor(*MEMBERS)
|
|
39
|
-
|
|
40
|
-
# Initialize all members to nil, so that they are defined and can be validated later
|
|
41
|
-
# @return [nil]
|
|
42
|
-
def initialize
|
|
43
|
-
MEMBERS.each{ |i| instance_variable_set(:"@#{i}", nil)}
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
# Validate that all members are set, raise exception if not
|
|
47
|
-
# @raise [Aspera::AssertionError] if any member is not set
|
|
48
|
-
# @return [nil]
|
|
49
|
-
def validate
|
|
50
|
-
MEMBERS.each do |i|
|
|
51
|
-
Aspera.assert(instance_variable_defined?(:"@#{i}"))
|
|
52
|
-
Aspera.assert(!instance_variable_get(:"@#{i}").nil?)
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
# Check if the context is in manual-only mode
|
|
57
|
-
# @return [Boolean] true if in manual-only mode
|
|
58
|
-
def only_manual?
|
|
59
|
-
transfer.eql?(:only_manual)
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
# Set the context to manual-only mode
|
|
63
|
-
# @return [Symbol] :only_manual
|
|
64
|
-
def only_manual!
|
|
65
|
-
@transfer = :only_manual
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
# The main CLI class
|
|
70
|
-
class Main
|
|
71
|
-
# Plugins store transfer result using this key and use result_transfer_multiple()
|
|
72
|
-
STATUS_FIELD = 'status'
|
|
73
|
-
COMMAND_CONFIG = :config
|
|
74
|
-
COMMAND_HELP = :help
|
|
75
|
-
# Types that go to result of type = text
|
|
76
|
-
SCALAR_TYPES = [String, Integer, Symbol].freeze
|
|
77
|
-
USER_INTERFACES = %i[text graphical].freeze
|
|
78
|
-
|
|
79
|
-
private_constant :COMMAND_CONFIG, :COMMAND_HELP, :SCALAR_TYPES, :USER_INTERFACES
|
|
80
|
-
|
|
81
|
-
class << self
|
|
82
|
-
# Create a special result type (only used internally here)
|
|
83
|
-
# @param special_sym [Symbol] the special result type
|
|
84
|
-
# @return [Hash] result hash with type :special
|
|
85
|
-
def result_special(special_sym); {type: :special, data: special_sym}; end
|
|
86
|
-
|
|
87
|
-
# Expect some list, but nothing to display
|
|
88
|
-
# @return [Hash] result hash for empty list
|
|
89
|
-
def result_empty; result_special(:empty); end
|
|
90
|
-
|
|
91
|
-
# Nothing expected
|
|
92
|
-
# @return [Hash] result hash for nothing
|
|
93
|
-
def result_nothing; result_special(:nothing); end
|
|
94
|
-
|
|
95
|
-
# Result is some status, such as "complete", "deleted"...
|
|
96
|
-
# @param status [String] The status
|
|
97
|
-
# @return [Hash] result hash with type :status
|
|
98
|
-
def result_status(status); return {type: :status, data: status}; end
|
|
99
|
-
|
|
100
|
-
# Text result coming from command result
|
|
101
|
-
# @param data [String, Integer, Symbol] the text data to display
|
|
102
|
-
# @return [Hash] result hash with type :text
|
|
103
|
-
def result_text(data); return {type: :text, data: data}; end
|
|
104
|
-
|
|
105
|
-
# Create a success result
|
|
106
|
-
# @return [Hash] result hash with status 'complete'
|
|
107
|
-
def result_success; return result_status('complete'); end
|
|
108
|
-
|
|
109
|
-
# Process statuses of finished transfer sessions
|
|
110
|
-
# @param statuses [Array] array of transfer session statuses
|
|
111
|
-
# @raise [Symbol] exception if there is one error
|
|
112
|
-
# @return [Hash] empty status result if all transfers succeeded
|
|
113
|
-
def result_transfer(statuses)
|
|
114
|
-
worst = TransferAgent.session_status(statuses)
|
|
115
|
-
raise worst unless worst.eql?(:success)
|
|
116
|
-
return Main.result_nothing
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
# Used when one command executes several transfer jobs (each job being possibly multi session)
|
|
120
|
-
# @param status_table [Array] [{STATUS_FIELD=>[status array],...},...]
|
|
121
|
-
# @return a status object suitable as command result
|
|
122
|
-
# Each element has a key STATUS_FIELD which contains the result of possibly multiple sessions
|
|
123
|
-
def result_transfer_multiple(status_table)
|
|
124
|
-
global_status = :success
|
|
125
|
-
# Transform status array into string and find if there was problem
|
|
126
|
-
status_table.each do |item|
|
|
127
|
-
worst = TransferAgent.session_status(item[STATUS_FIELD])
|
|
128
|
-
global_status = worst unless worst.eql?(:success)
|
|
129
|
-
item[STATUS_FIELD] = item[STATUS_FIELD].join(',')
|
|
130
|
-
end
|
|
131
|
-
raise global_status unless global_status.eql?(:success)
|
|
132
|
-
return result_object_list(status_table)
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
# Display image for that URL or directly blob
|
|
136
|
-
# @param url_or_blob [String] URL or blob to display as image
|
|
137
|
-
# @return [Hash] result hash with type :image
|
|
138
|
-
def result_image(url_or_blob)
|
|
139
|
-
return {type: :image, data: url_or_blob}
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
# A single object, must be Hash
|
|
143
|
-
# @param data [Hash] the object data
|
|
144
|
-
# @param fields [Array<String>, nil] optional list of fields to display
|
|
145
|
-
# @return [Hash] result hash with type :single_object
|
|
146
|
-
def result_single_object(data, fields: nil)
|
|
147
|
-
return {type: :single_object, data: data, fields: fields}
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
# An Array of Hash
|
|
151
|
-
# @param data [Array<Hash>] array of objects
|
|
152
|
-
# @param fields [Array<String>, nil] optional list of fields to display
|
|
153
|
-
# @param total [Integer, nil] optional total count
|
|
154
|
-
# @return [Hash] result hash with type :object_list
|
|
155
|
-
def result_object_list(data, fields: nil, total: nil)
|
|
156
|
-
return {type: :object_list, data: data, fields: fields, total: total}
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
# A list of values
|
|
160
|
-
# @param data [Array] The list of values
|
|
161
|
-
# @param name [String] The name of the list (used for display)
|
|
162
|
-
# @return [Hash] result hash with type :value_list
|
|
163
|
-
def result_value_list(data, name: 'id')
|
|
164
|
-
Aspera.assert_type(data, Array)
|
|
165
|
-
Aspera.assert_type(name, String)
|
|
166
|
-
return {type: :value_list, data: data, name: name}
|
|
167
|
-
end
|
|
168
|
-
|
|
169
|
-
# Determines type of result based on data
|
|
170
|
-
# @param data [Object] the data to analyze and format
|
|
171
|
-
# @return [Hash] result hash with appropriate type based on data
|
|
172
|
-
def result_auto(data)
|
|
173
|
-
case data
|
|
174
|
-
when NilClass
|
|
175
|
-
return result_special(:null)
|
|
176
|
-
when Hash
|
|
177
|
-
return result_single_object(data)
|
|
178
|
-
when Array
|
|
179
|
-
all_types = data.map(&:class).uniq
|
|
180
|
-
return result_object_list(data) if all_types.eql?([Hash])
|
|
181
|
-
unsupported_types = all_types - SCALAR_TYPES
|
|
182
|
-
return result_value_list(data, name: 'list') if unsupported_types.empty?
|
|
183
|
-
Aspera.error_unexpected_value(unsupported_types){'list item types'}
|
|
184
|
-
when *SCALAR_TYPES
|
|
185
|
-
return result_text(data)
|
|
186
|
-
else Aspera.error_unexpected_value(data.class.name){'result type'}
|
|
187
|
-
end
|
|
188
|
-
end
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
# Minimum initialization, no exception raised
|
|
192
|
-
# @param argv [Array<String>] command line arguments
|
|
193
|
-
# @return [nil]
|
|
194
|
-
def initialize(argv)
|
|
195
|
-
@argv = argv
|
|
196
|
-
Log.dump(:argv, @argv, level: :trace2)
|
|
197
|
-
@option_help = false
|
|
198
|
-
@option_show_config = false
|
|
199
|
-
@bash_completion = false
|
|
200
|
-
@context = Context.new
|
|
201
|
-
end
|
|
202
|
-
|
|
203
|
-
# This is the main function called by initial script just after constructor
|
|
204
|
-
# Processes command line arguments, executes commands, and handles exceptions
|
|
205
|
-
# @return [nil]
|
|
206
|
-
def process_command_line
|
|
207
|
-
# Catch exception information , if any
|
|
208
|
-
exception_info = nil
|
|
209
|
-
# False if command shall not be executed (e.g. --show-config)
|
|
210
|
-
execute_command = true
|
|
211
|
-
# Catch exceptions
|
|
212
|
-
begin
|
|
213
|
-
init_agents_and_options
|
|
214
|
-
# Find plugins, shall be after parse! ?
|
|
215
|
-
Plugins::Factory.instance.add_plugins_from_lookup_folders
|
|
216
|
-
# Help requested without command ? (plugins must be known here)
|
|
217
|
-
show_usage if @option_help && @context.options.command_or_arg_empty?
|
|
218
|
-
generate_bash_completion if @bash_completion
|
|
219
|
-
@context.config.periodic_check_newer_gem_version
|
|
220
|
-
command_sym =
|
|
221
|
-
if @option_show_config && @context.options.command_or_arg_empty?
|
|
222
|
-
COMMAND_CONFIG
|
|
223
|
-
else
|
|
224
|
-
@context.options.get_next_command(Plugins::Factory.instance.plugin_list.unshift(COMMAND_HELP))
|
|
225
|
-
end
|
|
226
|
-
# Command will not be executed, but we need manual
|
|
227
|
-
@context.options.fail_on_missing_mandatory = false if @option_help || @option_show_config
|
|
228
|
-
# Main plugin is not dynamically instantiated
|
|
229
|
-
case command_sym
|
|
230
|
-
when COMMAND_HELP
|
|
231
|
-
show_usage
|
|
232
|
-
when COMMAND_CONFIG
|
|
233
|
-
command_plugin = @context.config
|
|
234
|
-
else
|
|
235
|
-
# Get plugin, set options, etc
|
|
236
|
-
command_plugin = get_plugin_instance_with_options(command_sym)
|
|
237
|
-
# Parse plugin specific options
|
|
238
|
-
@context.options.parse_options!
|
|
239
|
-
end
|
|
240
|
-
# Help requested for current plugin
|
|
241
|
-
show_usage(all: false) if @option_help
|
|
242
|
-
if @option_show_config
|
|
243
|
-
@context.formatter.display_results(type: :single_object, data: @context.options.known_options(only_defined: true).stringify_keys)
|
|
244
|
-
execute_command = false
|
|
245
|
-
end
|
|
246
|
-
# Locking for single execution (only after "per plugin" option, in case lock port is there)
|
|
247
|
-
lock_port = @context.options.get_option(:lock_port)
|
|
248
|
-
if !lock_port.nil?
|
|
249
|
-
begin
|
|
250
|
-
# No need to close later, will be freed on process exit. must save in member else it is garbage collected
|
|
251
|
-
Log.log.debug{"Opening lock port #{lock_port}"}
|
|
252
|
-
# Loopback address, could also be 'localhost'
|
|
253
|
-
@tcp_server = TCPServer.new('127.0.0.1', lock_port)
|
|
254
|
-
rescue StandardError => e
|
|
255
|
-
execute_command = false
|
|
256
|
-
Log.log.warn{"Another instance is already running (#{e.message})."}
|
|
257
|
-
end
|
|
258
|
-
end
|
|
259
|
-
pid_file = @context.options.get_option(:pid_file)
|
|
260
|
-
if !pid_file.nil?
|
|
261
|
-
File.write(pid_file, Process.pid)
|
|
262
|
-
Log.log.debug{"Wrote pid #{Process.pid} to #{pid_file}"}
|
|
263
|
-
at_exit{File.delete(pid_file)}
|
|
264
|
-
end
|
|
265
|
-
# Execute and display (if not exclusive execution)
|
|
266
|
-
@context.formatter.display_results(**command_plugin.execute_action) if execute_command
|
|
267
|
-
# Save config file if command modified it
|
|
268
|
-
@context.config.save_config_file_if_needed
|
|
269
|
-
# Finish
|
|
270
|
-
@context.transfer.shutdown
|
|
271
|
-
rescue Net::SSH::AuthenticationFailed => e; exception_info = {e: e, t: 'SSH', security: true}
|
|
272
|
-
rescue OpenSSL::SSL::SSLError => e; exception_info = {e: e, t: 'SSL'}
|
|
273
|
-
rescue Cli::BadArgument => e; exception_info = {e: e, t: 'Argument', usage: true}
|
|
274
|
-
rescue Cli::MissingArgument => e; exception_info = {e: e, t: 'Missing', usage: true}
|
|
275
|
-
rescue Cli::BadIdentifier => e; exception_info = {e: e, t: 'Identifier'}
|
|
276
|
-
rescue Cli::SchemaRequest => e; exception_info = {e: e, t: 'Schema'}
|
|
277
|
-
rescue Cli::Error => e; exception_info = {e: e, t: 'Tool', usage: true}
|
|
278
|
-
rescue Transfer::Error => e; exception_info = {e: e, t: 'Transfer'}
|
|
279
|
-
rescue RestCallError => e; exception_info = {e: e, t: 'Rest'}
|
|
280
|
-
rescue SocketError => e; exception_info = {e: e, t: 'Network'}
|
|
281
|
-
rescue StandardError => e; exception_info = {e: e, t: "Other(#{e.class.name})", debug: true}
|
|
282
|
-
rescue Interrupt => e; exception_info = {e: e, t: 'Interruption', debug: true}
|
|
283
|
-
end
|
|
284
|
-
# Cleanup file list files
|
|
285
|
-
TempFileManager.instance.cleanup
|
|
286
|
-
# 1- processing of error condition
|
|
287
|
-
unless exception_info.nil?
|
|
288
|
-
Log.log.warn(exception_info[:e].message) if Log.instance.logger_type.eql?(:syslog) && exception_info[:security]
|
|
289
|
-
Log.log.error{"#{exception_info[:t]}: #{exception_info[:e].message}"} unless exception_info[:e].is_a?(Cli::SchemaRequest)
|
|
290
|
-
Log.log.debug{(['Backtrace:'] + exception_info[:e].backtrace).join("\n")} if exception_info[:debug]
|
|
291
|
-
@context.formatter.display_message(:error, 'Use option -h to get help.') if exception_info[:usage]
|
|
292
|
-
# Is that a known error condition with proposal for remediation ?
|
|
293
|
-
Hints.hint_for(exception_info[:e], @context.formatter)
|
|
294
|
-
# Requested help for a Hash parameter/option ?
|
|
295
|
-
if exception_info[:e].is_a?(Cli::SchemaRequest)
|
|
296
|
-
Log.log.info{"#{exception_info[:t]}: #{exception_info[:e].message}"}
|
|
297
|
-
schema_path = exception_info[:e].path
|
|
298
|
-
if schema_path.nil?
|
|
299
|
-
Log.log.warn{'Sorry, no schema provided yet. Please refer to the manual or API.'}
|
|
300
|
-
else
|
|
301
|
-
builder = Schema::Documentation.new(TerminalFormatter, Schema::Registry.instance.reader(schema_path)).build
|
|
302
|
-
@context.formatter.display_results(**Main.result_object_list(builder.rows, fields: builder.columns))
|
|
303
|
-
end
|
|
304
|
-
end
|
|
305
|
-
end
|
|
306
|
-
# 2- processing of command not processed (due to exception or bad command line)
|
|
307
|
-
if execute_command || @option_show_config
|
|
308
|
-
@context.options.final_errors.each do |msg|
|
|
309
|
-
Log.log.error{"Argument: #{msg}"}
|
|
310
|
-
# Add code as exception if there is not already an error
|
|
311
|
-
exception_info = {e: Exception.new(msg), t: 'UnusedArg'} if exception_info.nil?
|
|
312
|
-
end
|
|
313
|
-
end
|
|
314
|
-
# 3- in case of error, fail the process status
|
|
315
|
-
unless exception_info.nil?
|
|
316
|
-
# Show stack trace in debug mode
|
|
317
|
-
raise exception_info[:e] if Log.log.debug?
|
|
318
|
-
# Else give hint and exit
|
|
319
|
-
@context.formatter.display_message(:error, 'Use --log-level=debug to get more details.') if exception_info[:debug]
|
|
320
|
-
Process.exit(1)
|
|
321
|
-
end
|
|
322
|
-
return
|
|
323
|
-
end
|
|
324
|
-
|
|
325
|
-
# Display usage information and help
|
|
326
|
-
# @param all [Boolean] if true, show help for all plugins; if false, show only current plugin
|
|
327
|
-
# @param exit [Boolean] if true, exit the process after displaying help
|
|
328
|
-
# @return [nil]
|
|
329
|
-
def show_usage(all: true, exit: true)
|
|
330
|
-
# Display main plugin options (+config)
|
|
331
|
-
@context.formatter.display_message(:error, @context.options.parser)
|
|
332
|
-
if all
|
|
333
|
-
@context.only_manual!
|
|
334
|
-
# List plugins that have a "require" field, i.e. all but main plugin
|
|
335
|
-
Plugins::Factory.instance.plugin_list.each do |plugin_name_sym|
|
|
336
|
-
# Config was already included in the global options
|
|
337
|
-
next if plugin_name_sym.eql?(COMMAND_CONFIG)
|
|
338
|
-
# Override main option parser with a brand new, to avoid having global options
|
|
339
|
-
@context.options = Manager.new(Info::CMD_NAME)
|
|
340
|
-
@context.options.parser.banner = '' # Remove default banner
|
|
341
|
-
get_plugin_instance_with_options(plugin_name_sym)
|
|
342
|
-
# Display generated help for plugin options
|
|
343
|
-
@context.formatter.display_message(:error, @context.options.parser.help)
|
|
344
|
-
end
|
|
345
|
-
end
|
|
346
|
-
Process.exit(0) if exit
|
|
347
|
-
end
|
|
348
|
-
|
|
349
|
-
private
|
|
350
|
-
|
|
351
|
-
# Initialize agents and options
|
|
352
|
-
# This can throw exception if there is a problem with the environment, needs to be caught by execute method
|
|
353
|
-
# @raise [StandardError] if there is a problem with the environment
|
|
354
|
-
# @return [nil]
|
|
355
|
-
def init_agents_and_options
|
|
356
|
-
@context.man_header = true
|
|
357
|
-
# Create formatter, in case there is an exception, it is used to display.
|
|
358
|
-
@context.formatter = Formatter.new
|
|
359
|
-
# Create command line manager with arguments
|
|
360
|
-
@context.options = Manager.new(Info::CMD_NAME, @argv)
|
|
361
|
-
# Formatter adds options
|
|
362
|
-
@context.formatter.declare_options(@context.options)
|
|
363
|
-
# Compare $0 with expected name
|
|
364
|
-
current_prog_name = File.basename($PROGRAM_NAME)
|
|
365
|
-
Aspera.assert(current_prog_name.eql?(Info::CMD_NAME), type: :warn){"Please use '#{Info::CMD_NAME}' instead of '#{current_prog_name}'"}
|
|
366
|
-
# Declare and parse global options
|
|
367
|
-
declare_global_options
|
|
368
|
-
# Do not display config commands if help is asked
|
|
369
|
-
@context.man_header = false
|
|
370
|
-
# The Config plugin adds the @preset parser, so declare before TransferAgent which may use it
|
|
371
|
-
@context.config = Plugins::Config.new(context: @context)
|
|
372
|
-
@context.man_header = true
|
|
373
|
-
# Data persistency is set in config
|
|
374
|
-
Aspera.assert(@context.persistency){'missing persistency object'}
|
|
375
|
-
# The TransferAgent plugin may use the @preset parser
|
|
376
|
-
@context.transfer = TransferAgent.new(@context.options, @context.config)
|
|
377
|
-
# Add commands for config plugin after all options have been added
|
|
378
|
-
@context.config.add_manual_header(false)
|
|
379
|
-
@context.validate
|
|
380
|
-
# Set banner when all environment is created so that additional extended value modifiers are known, e.g. @preset
|
|
381
|
-
@context.options.parser.banner = app_banner
|
|
382
|
-
end
|
|
383
|
-
|
|
384
|
-
# Generate the application banner for help display
|
|
385
|
-
# @return [String] formatted banner text
|
|
386
|
-
def app_banner
|
|
387
|
-
t = ' ' * 8
|
|
388
|
-
return <<~END_OF_BANNER
|
|
389
|
-
NAME
|
|
390
|
-
#{t}#{Info::CMD_NAME} -- a command line tool for Aspera Applications (v#{Cli::VERSION})
|
|
391
|
-
|
|
392
|
-
SYNOPSIS
|
|
393
|
-
#{t}#{Info::CMD_NAME} COMMANDS [OPTIONS] [ARGS]
|
|
394
|
-
|
|
395
|
-
DESCRIPTION
|
|
396
|
-
#{t}Use Aspera application to perform operations on command line.
|
|
397
|
-
#{t}Documentation and examples: #{Info::GEM_URL}
|
|
398
|
-
#{t}execute: #{Info::CMD_NAME} conf doc
|
|
399
|
-
#{t}or visit: #{Info::DOC_URL}
|
|
400
|
-
#{t}source repo: #{Info::SRC_URL}
|
|
401
|
-
|
|
402
|
-
ENVIRONMENT VARIABLES
|
|
403
|
-
#{t}Any option can be set as an environment variable, refer to the manual
|
|
404
|
-
|
|
405
|
-
COMMANDS
|
|
406
|
-
#{t}To list first level commands, execute: #{Info::CMD_NAME}
|
|
407
|
-
#{t}Note that commands can be written shortened (provided it is unique).
|
|
408
|
-
|
|
409
|
-
OPTIONS
|
|
410
|
-
#{t}Options begin with a '-' (minus), and value is provided on command line.
|
|
411
|
-
#{t}Special values are supported beginning with special prefix @pfx:, where pfx is one of:
|
|
412
|
-
#{t}#{ExtendedValue.instance.modifiers.join(', ')}
|
|
413
|
-
#{t}Dates format is 'DD-MM-YY HH:MM:SS', or 'now' or '-<num>h'
|
|
414
|
-
|
|
415
|
-
ARGS
|
|
416
|
-
#{t}Some commands require mandatory arguments, e.g. a path.
|
|
417
|
-
END_OF_BANNER
|
|
418
|
-
end
|
|
419
|
-
|
|
420
|
-
# Define header for manual and declare all global options
|
|
421
|
-
# @return [nil]
|
|
422
|
-
def declare_global_options
|
|
423
|
-
Log.log.debug('declare_global_options')
|
|
424
|
-
@context.options.declare(:help, 'Show this message', allowed: Allowed::TYPES_NONE, short: 'h'){@option_help = true}
|
|
425
|
-
@context.options.declare(:bash_comp, 'Generate bash completion for command', allowed: Allowed::TYPES_NONE){@bash_completion = true}
|
|
426
|
-
@context.options.declare(:show_config, 'Display parameters used for the provided action', allowed: Allowed::TYPES_NONE){@option_show_config = true}
|
|
427
|
-
@context.options.declare(:version, 'Display version', allowed: Allowed::TYPES_NONE, short: 'v'){@context.formatter.display_message(:data, Cli::VERSION); Process.exit(0)} # rubocop:disable Style/Semicolon
|
|
428
|
-
@context.options.declare(
|
|
429
|
-
:ui, 'Method to start browser',
|
|
430
|
-
allowed: USER_INTERFACES,
|
|
431
|
-
handler: {o: Environment.instance, m: :url_method}
|
|
432
|
-
)
|
|
433
|
-
@context.options.declare(
|
|
434
|
-
:invalid_characters, 'Replacement character and invalid filename characters',
|
|
435
|
-
handler: {o: Environment.instance, m: :file_illegal_characters}
|
|
436
|
-
)
|
|
437
|
-
@context.options.declare(:log_level, 'Log level', allowed: Log::LEVELS, handler: {o: Log.instance, m: :level})
|
|
438
|
-
@context.options.declare(:log_format, 'Log formatter', allowed: [Proc, Logger::Formatter, String], handler: {o: Log.instance, m: :formatter})
|
|
439
|
-
@context.options.declare(:logger, 'Logging method', allowed: Log::LOG_TYPES, handler: {o: Log.instance, m: :logger_type})
|
|
440
|
-
@context.options.declare(:lock_port, 'Prevent dual execution of a command, e.g. in cron', allowed: Allowed::TYPES_INTEGER)
|
|
441
|
-
@context.options.declare(:once_only, 'Process only new items (some commands)', allowed: Allowed::TYPES_BOOLEAN, default: false)
|
|
442
|
-
@context.options.declare(:log_secrets, 'Show passwords in logs', allowed: Allowed::TYPES_BOOLEAN, handler: {o: SecretHider.instance, m: :log_secrets})
|
|
443
|
-
@context.options.declare(:clean_temp, 'Cleanup temporary files on exit', allowed: Allowed::TYPES_BOOLEAN, handler: {o: TempFileManager.instance, m: :cleanup_on_exit})
|
|
444
|
-
@context.options.declare(:temp_folder, 'Temporary folder', handler: {o: TempFileManager.instance, m: :global_temp})
|
|
445
|
-
@context.options.declare(:pid_file, 'Write process identifier to file, delete on exit')
|
|
446
|
-
@context.options.declare(
|
|
447
|
-
:parser, 'Default parser for structured parameters and options',
|
|
448
|
-
handler: {o: ExtendedValue.instance, m: :default_decoder},
|
|
449
|
-
allowed: ExtendedValue::DEFAULT_DECODERS,
|
|
450
|
-
default: ExtendedValue::DEFAULT_DECODERS.first
|
|
451
|
-
)
|
|
452
|
-
# Parse declared options
|
|
453
|
-
@context.options.parse_options!
|
|
454
|
-
end
|
|
455
|
-
|
|
456
|
-
# Get the plugin instance based on name
|
|
457
|
-
# Also loads the plugin options, and default values from conf file
|
|
458
|
-
# @param plugin_name_sym [Symbol] symbol for plugin name
|
|
459
|
-
# @return [Plugins::Base] the plugin instance
|
|
460
|
-
def get_plugin_instance_with_options(plugin_name_sym)
|
|
461
|
-
Log.log.debug{"get_plugin_instance_with_options(#{plugin_name_sym})"}
|
|
462
|
-
# Load default params only if no param already loaded before plugin instantiation
|
|
463
|
-
@context.config.add_plugin_default_preset(plugin_name_sym)
|
|
464
|
-
command_plugin = Plugins::Factory.instance.create(plugin_name_sym, context: @context)
|
|
465
|
-
return command_plugin
|
|
466
|
-
end
|
|
467
|
-
|
|
468
|
-
# Generate bash completion suggestions
|
|
469
|
-
# @return [nil]
|
|
470
|
-
def generate_bash_completion
|
|
471
|
-
if @context.options.get_next_argument('', multiple: true, mandatory: false).nil?
|
|
472
|
-
Plugins::Factory.instance.plugin_list.each{ |p| puts p}
|
|
473
|
-
else
|
|
474
|
-
Log.log.warn('only first level completion so far')
|
|
475
|
-
end
|
|
476
|
-
Process.exit(0)
|
|
477
|
-
end
|
|
478
|
-
end
|
|
479
|
-
end
|
|
480
|
-
end
|
data/lib/aspera/json_rpc.rb
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# cspell:ignore blankslate
|
|
4
|
-
|
|
5
|
-
require 'aspera/rest_error_analyzer'
|
|
6
|
-
require 'aspera/assert'
|
|
7
|
-
require 'blankslate'
|
|
8
|
-
|
|
9
|
-
Aspera::RestErrorAnalyzer.instance.add_simple_handler(name: 'JSON RPC', path: %w[error message], always: true)
|
|
10
|
-
|
|
11
|
-
module Aspera
|
|
12
|
-
# a very simple JSON RPC client
|
|
13
|
-
class JsonRpcClient < BlankSlate
|
|
14
|
-
JSON_RPC_VERSION = '2.0'
|
|
15
|
-
reveal :instance_variable_get
|
|
16
|
-
reveal :inspect
|
|
17
|
-
reveal :to_s
|
|
18
|
-
|
|
19
|
-
def initialize(api, namespace = nil)
|
|
20
|
-
super()
|
|
21
|
-
@api = api
|
|
22
|
-
@namespace = namespace
|
|
23
|
-
@request_id = 0
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def respond_to_missing?(_sym, _include_private = false)
|
|
27
|
-
true
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def method_missing(method, *args, &block)
|
|
31
|
-
args = args.first if args.size == 1 && args.first.is_a?(Hash)
|
|
32
|
-
data = @api.create('', {
|
|
33
|
-
jsonrpc: JSON_RPC_VERSION,
|
|
34
|
-
method: "#{@namespace}#{method}",
|
|
35
|
-
params: args,
|
|
36
|
-
id: @request_id += 1
|
|
37
|
-
})
|
|
38
|
-
Aspera.assert_type(data, Hash){'response'}
|
|
39
|
-
Aspera.assert(data['jsonrpc'] == JSON_RPC_VERSION){'bad version in response'}
|
|
40
|
-
Aspera.assert(data.key?('id')){'missing id in response'}
|
|
41
|
-
Aspera.assert(!(data.key?('error') && data.key?('result'))){'both error and response'}
|
|
42
|
-
Aspera.assert(
|
|
43
|
-
!data.key?('error') ||
|
|
44
|
-
data['error'].is_a?(Hash) &&
|
|
45
|
-
data['error']['code'].is_a?(Integer) &&
|
|
46
|
-
data['error']['message'].is_a?(String)
|
|
47
|
-
){'bad error response'}
|
|
48
|
-
return data['result']
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
end
|