aspera-cli 4.26.0 → 4.26.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +36 -4
- data/CONTRIBUTING.md +2 -5
- data/bin/ascli +2 -2
- data/lib/aspera/agent/direct.rb +1 -1
- data/lib/aspera/agent/factory.rb +4 -0
- data/lib/aspera/agent/httpgw.rb +1 -1
- data/lib/aspera/agent/node.rb +2 -2
- data/lib/aspera/api/aoc.rb +45 -37
- data/lib/aspera/api/cos_node.rb +1 -1
- data/lib/aspera/api/faspex.rb +7 -6
- data/lib/aspera/api/node.rb +9 -9
- data/lib/aspera/ascmd.rb +4 -4
- data/lib/aspera/ascp/installation.rb +13 -6
- data/lib/aspera/assert.rb +17 -13
- data/lib/aspera/cli/context.rb +58 -0
- data/lib/aspera/cli/extended_value.rb +9 -4
- data/lib/aspera/cli/formatter.rb +91 -187
- data/lib/aspera/cli/http.rb +167 -0
- data/lib/aspera/cli/manager.rb +132 -77
- data/lib/aspera/cli/options.schema.yaml +82 -0
- data/lib/aspera/cli/plugins/alee.rb +2 -2
- data/lib/aspera/cli/plugins/aoc.rb +113 -77
- data/lib/aspera/cli/plugins/ats.rb +16 -16
- data/lib/aspera/cli/plugins/base.rb +64 -49
- data/lib/aspera/cli/plugins/config.rb +172 -444
- data/lib/aspera/cli/plugins/console.rb +7 -7
- data/lib/aspera/cli/plugins/cos.rb +2 -2
- data/lib/aspera/cli/plugins/factory.rb +3 -0
- data/lib/aspera/cli/plugins/faspex.rb +22 -22
- data/lib/aspera/cli/plugins/faspex5.rb +68 -49
- data/lib/aspera/cli/plugins/faspio.rb +1 -1
- data/lib/aspera/cli/plugins/httpgw.rb +2 -2
- data/lib/aspera/cli/plugins/node.rb +80 -80
- data/lib/aspera/cli/plugins/orchestrator.rb +21 -26
- data/lib/aspera/cli/plugins/preview.rb +9 -9
- data/lib/aspera/cli/plugins/server.rb +7 -7
- data/lib/aspera/cli/plugins/shares.rb +2 -2
- data/lib/aspera/cli/preset_manager.rb +235 -0
- data/lib/aspera/cli/result.rb +310 -0
- data/lib/aspera/cli/{main.rb → runner.rb} +48 -130
- data/lib/aspera/cli/sync_actions.rb +15 -11
- data/lib/aspera/cli/terminal_formatter.rb +65 -0
- data/lib/aspera/cli/transfer_agent.rb +17 -15
- data/lib/aspera/cli/transfer_progress.rb +6 -6
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/cli/wizard.rb +5 -6
- data/lib/aspera/command_line_builder.rb +22 -18
- data/lib/aspera/data_repository.rb +4 -0
- data/lib/aspera/dot_container.rb +1 -1
- data/lib/aspera/environment.rb +25 -6
- data/lib/aspera/faspex_gw.rb +1 -1
- data/lib/aspera/formatter_interface.rb +14 -0
- data/lib/aspera/hash_ext.rb +6 -0
- data/lib/aspera/json_rpc.rb +6 -5
- data/lib/aspera/keychain/base.rb +1 -1
- data/lib/aspera/keychain/encrypted_hash.rb +1 -1
- data/lib/aspera/keychain/factory.rb +1 -1
- data/lib/aspera/keychain/macos_security.rb +1 -1
- data/lib/aspera/log.rb +10 -5
- data/lib/aspera/markdown.rb +4 -1
- data/lib/aspera/nagios.rb +2 -2
- data/lib/aspera/oauth/base.rb +5 -5
- data/lib/aspera/oauth/boot.rb +43 -0
- data/lib/aspera/oauth/factory.rb +38 -14
- data/lib/aspera/oauth/web.rb +2 -2
- data/lib/aspera/oauth.rb +1 -0
- data/lib/aspera/persistency_action_once.rb +2 -2
- data/lib/aspera/preview/file_types.rb +4 -0
- data/lib/aspera/products/connect.rb +3 -0
- data/lib/aspera/products/transferd.rb +2 -2
- data/lib/aspera/proxy_auto_config.rb +6 -3
- data/lib/aspera/rest.rb +9 -5
- data/lib/aspera/rest_error_analyzer.rb +4 -0
- data/lib/aspera/rest_list.rb +10 -3
- data/lib/aspera/schema/IBM Aspera Faspex API-5.0-enhanced.yaml +62811 -0
- data/lib/aspera/schema/IBM Aspera on Cloud API-0.2.6-enhanced.yaml +9637 -0
- data/lib/aspera/schema/async_tables.yaml +361 -0
- data/lib/aspera/schema/documentation.rb +107 -0
- data/lib/aspera/schema/reader.rb +75 -0
- data/lib/aspera/schema/registry.rb +70 -0
- data/lib/aspera/secret_hider.rb +4 -0
- data/lib/aspera/sync/conf.schema.yaml +0 -26
- data/lib/aspera/sync/database.rb +14 -10
- data/lib/aspera/sync/operations.rb +14 -10
- data/lib/aspera/temp_file_manager.rb +4 -0
- data/lib/aspera/transfer/faux_file.rb +1 -1
- data/lib/aspera/transfer/parameters.rb +4 -4
- data/lib/aspera/transfer/resumer.rb +2 -2
- data/lib/aspera/transfer/spec.rb +3 -3
- data/lib/aspera/transfer/spec.schema.yaml +18 -9
- data/lib/aspera/transfer/uri.rb +1 -1
- data/lib/aspera/uri_reader.rb +1 -1
- data/lib/aspera/yaml.rb +4 -2
- data.tar.gz.sig +0 -0
- metadata +28 -15
- metadata.gz.sig +1 -1
- data/lib/aspera/transfer/spec_doc.rb +0 -76
data/lib/aspera/cli/manager.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require 'aspera/cli/extended_value'
|
|
4
4
|
require 'aspera/cli/error'
|
|
5
|
+
require 'aspera/cli/terminal_formatter'
|
|
5
6
|
require 'aspera/colors'
|
|
6
7
|
require 'aspera/secret_hider'
|
|
7
8
|
require 'aspera/log'
|
|
@@ -12,6 +13,20 @@ require 'optparse'
|
|
|
12
13
|
|
|
13
14
|
module Aspera
|
|
14
15
|
module Cli
|
|
16
|
+
# Exception raised when schema is asked (`help`)
|
|
17
|
+
class SchemaRequest < Error
|
|
18
|
+
# @return [String, nil] path to schema file
|
|
19
|
+
attr_reader :path
|
|
20
|
+
|
|
21
|
+
# @param type [Symbol] :argument or :option
|
|
22
|
+
# @param name [String] name of the option/argument
|
|
23
|
+
# @param schema_path [String, nil] path to schema file, or `nil` if not available
|
|
24
|
+
def initialize(type, name, schema_path)
|
|
25
|
+
super("#{type}: #{name}")
|
|
26
|
+
@path = schema_path
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
15
30
|
module BoolValue
|
|
16
31
|
# boolean options are set to true/false from the following values
|
|
17
32
|
YES_SYM = :yes
|
|
@@ -22,23 +37,24 @@ module Aspera
|
|
|
22
37
|
# Boolean values
|
|
23
38
|
# @return [Array<true, false, :yes, :no>]
|
|
24
39
|
ALL = (TRUE_VALUES + FALSE_VALUES).freeze
|
|
40
|
+
# `false` and `true`
|
|
25
41
|
TYPES = [FalseClass, TrueClass].freeze
|
|
26
42
|
SYMBOLS = [NO_SYM, YES_SYM].freeze
|
|
27
43
|
# @return `true` if value is a value for `true` in ALL
|
|
28
44
|
def true?(enum)
|
|
29
45
|
Aspera.assert_values(enum, ALL){'boolean'}
|
|
30
|
-
|
|
46
|
+
TRUE_VALUES.include?(enum)
|
|
31
47
|
end
|
|
32
48
|
|
|
33
49
|
# @return [:yes, :no]
|
|
34
50
|
def to_sym(enum)
|
|
35
51
|
Aspera.assert_values(enum, ALL){'boolean'}
|
|
36
|
-
|
|
52
|
+
TRUE_VALUES.include?(enum) ? YES_SYM : NO_SYM
|
|
37
53
|
end
|
|
38
54
|
|
|
39
55
|
# @return `true` if value is a value for `true` or `false` in ALL
|
|
40
56
|
def symbol?(sym)
|
|
41
|
-
|
|
57
|
+
ALL.include?(sym)
|
|
42
58
|
end
|
|
43
59
|
module_function :true?, :to_sym, :symbol?
|
|
44
60
|
end
|
|
@@ -52,30 +68,33 @@ module Aspera
|
|
|
52
68
|
# Value will be coerced to int
|
|
53
69
|
TYPES_INTEGER = [Integer].freeze
|
|
54
70
|
TYPES_BOOLEAN = BoolValue::TYPES
|
|
55
|
-
#
|
|
71
|
+
# No value at all for the option, it's a switch, like `-N`
|
|
56
72
|
TYPES_NONE = [].freeze
|
|
73
|
+
# Symbol
|
|
57
74
|
TYPES_ENUM = [Symbol].freeze
|
|
75
|
+
# String
|
|
58
76
|
TYPES_STRING = [String].freeze
|
|
59
77
|
end
|
|
60
78
|
|
|
61
79
|
# Description of option, how to manage
|
|
62
80
|
class OptionValue
|
|
63
81
|
# [Array(Class)] List of allowed types
|
|
64
|
-
attr_reader :types, :sensitive
|
|
82
|
+
attr_reader :types, :sensitive, :schema, :option
|
|
65
83
|
# [Array] List of allowed values (Symbols and specific values)
|
|
66
84
|
attr_accessor :values
|
|
67
85
|
|
|
68
|
-
# @param option
|
|
86
|
+
# @param option [Symbol] Name of option
|
|
69
87
|
# @param description [String] Description for help
|
|
70
|
-
# @param allowed
|
|
71
|
-
# @param handler
|
|
88
|
+
# @param allowed [nil,Class,Array<Class>,Array<Symbol>] Allowed values
|
|
89
|
+
# @param handler [Hash] Accessor: keys: :o(object) and :m(method)
|
|
72
90
|
# @param deprecation [String] Deprecation message
|
|
91
|
+
# @param schema [String] Declaration of schema
|
|
73
92
|
# `allowed`:
|
|
74
93
|
# - `nil` No validation, so just a string
|
|
75
94
|
# - `Class` The single allowed Class
|
|
76
95
|
# - `Array<Class>` Multiple allowed classes
|
|
77
96
|
# - `Array<Symbol>` List of allowed values
|
|
78
|
-
def initialize(option:, description:, allowed: Allowed::TYPES_STRING, handler: nil, deprecation: nil)
|
|
97
|
+
def initialize(option:, description:, allowed: Allowed::TYPES_STRING, handler: nil, deprecation: nil, schema: nil)
|
|
79
98
|
Log.log.trace1{"option: #{option}, allowed: #{allowed}"}
|
|
80
99
|
@option = option
|
|
81
100
|
@description = description
|
|
@@ -86,6 +105,7 @@ module Aspera
|
|
|
86
105
|
@read_method = handler&.[](:m)
|
|
87
106
|
@write_method = @read_method ? "#{@read_method}=".to_sym : nil
|
|
88
107
|
@deprecation = deprecation
|
|
108
|
+
@schema = schema
|
|
89
109
|
@access = if @object.nil?
|
|
90
110
|
:local
|
|
91
111
|
elsif @object.respond_to?(@write_method)
|
|
@@ -102,7 +122,7 @@ module Aspera
|
|
|
102
122
|
if allowed.take(Allowed::TYPES_SYMBOL_ARRAY.length) == Allowed::TYPES_SYMBOL_ARRAY
|
|
103
123
|
# Special case: array of defined symbol values
|
|
104
124
|
@types = Allowed::TYPES_SYMBOL_ARRAY
|
|
105
|
-
@values = allowed[Allowed::TYPES_SYMBOL_ARRAY.length
|
|
125
|
+
@values = allowed[Allowed::TYPES_SYMBOL_ARRAY.length..]
|
|
106
126
|
elsif allowed.all?(Class)
|
|
107
127
|
@types = allowed
|
|
108
128
|
@values = BoolValue::ALL if allowed.eql?(Allowed::TYPES_BOOLEAN)
|
|
@@ -131,12 +151,14 @@ module Aspera
|
|
|
131
151
|
when :setter then @object.send(@read_method, @option, :get)
|
|
132
152
|
end
|
|
133
153
|
Log.log.trace1{"#{@option} -> (#{current_value.class})#{current_value}"} if log
|
|
134
|
-
|
|
154
|
+
current_value
|
|
135
155
|
end
|
|
136
156
|
|
|
137
157
|
# Assign value to option.
|
|
138
|
-
# Value can be a String
|
|
158
|
+
# Value can be a `String`, then evaluated with `ExtendedValue`, or directly a value.
|
|
139
159
|
# @param value [String, Object] Value to assign to option
|
|
160
|
+
# @param where [String] Where the value is assigned from
|
|
161
|
+
# @return [nil]
|
|
140
162
|
def assign_value(value, where:)
|
|
141
163
|
Aspera.assert(!@deprecation, type: warn){"Option #{@option} is deprecated: #{@deprecation}"}
|
|
142
164
|
new_value = ExtendedValue.instance.evaluate(value, context: "option: #{@option}", allowed: @types)
|
|
@@ -150,7 +172,6 @@ module Aspera
|
|
|
150
172
|
new_value = [] if new_value.eql?(nil) && @types&.first.eql?(Array)
|
|
151
173
|
if @types.eql?(Aspera::Cli::Allowed::TYPES_SYMBOL_ARRAY)
|
|
152
174
|
new_value = [new_value] if new_value.is_a?(String)
|
|
153
|
-
Aspera.assert_type(new_value, Array, type: BadArgument)
|
|
154
175
|
Aspera.assert_array_all(new_value, String, type: BadArgument)
|
|
155
176
|
new_value = new_value.map{ |v| Manager.get_from_list(v, @option, @values)}
|
|
156
177
|
end
|
|
@@ -166,6 +187,7 @@ module Aspera
|
|
|
166
187
|
when :setter then @object.send(@read_method, @option, :set, new_value)
|
|
167
188
|
end
|
|
168
189
|
Log.log.trace1{v = value(log: false); "#{@option} <- (#{v.class})#{v}"} # rubocop:disable Style/Semicolon
|
|
190
|
+
nil
|
|
169
191
|
end
|
|
170
192
|
end
|
|
171
193
|
|
|
@@ -184,25 +206,25 @@ module Aspera
|
|
|
184
206
|
Aspera.assert(!matching.empty?, multi_choice_assert_msg("unknown value for #{descr}: #{short_value}", allowed_values), type: BadArgument)
|
|
185
207
|
Aspera.assert(matching.length.eql?(1), multi_choice_assert_msg("ambiguous shortcut for #{descr}: #{short_value}", matching), type: BadArgument)
|
|
186
208
|
return BoolValue.true?(matching.first) if allowed_values.eql?(BoolValue::ALL)
|
|
187
|
-
|
|
209
|
+
matching.first
|
|
188
210
|
end
|
|
189
211
|
|
|
190
212
|
# Generates error message with list of allowed values
|
|
191
|
-
# @param error_msg [String]
|
|
192
|
-
# @param accept_list [Array]
|
|
213
|
+
# @param error_msg [String] Error message
|
|
214
|
+
# @param accept_list [Array<Symbol>] List of allowed values
|
|
193
215
|
def multi_choice_assert_msg(error_msg, accept_list)
|
|
194
|
-
[error_msg, 'Use:'
|
|
216
|
+
[error_msg, 'Use:', *accept_list.map{ |choice| "- #{choice}"}.sort].join("\n")
|
|
195
217
|
end
|
|
196
218
|
|
|
197
219
|
# Change option name with dash to name with underscore
|
|
198
220
|
# @param name [String] option name
|
|
199
221
|
# @return [String]
|
|
200
222
|
def option_line_to_name(name)
|
|
201
|
-
|
|
223
|
+
name.gsub(OPTION_SEP_LINE, OPTION_SEP_SYMBOL)
|
|
202
224
|
end
|
|
203
225
|
|
|
204
226
|
def option_name_to_line(name)
|
|
205
|
-
|
|
227
|
+
"#{OPTION_PREFIX}#{name.to_s.gsub(OPTION_SEP_SYMBOL, OPTION_SEP_LINE)}"
|
|
206
228
|
end
|
|
207
229
|
|
|
208
230
|
# @return [Hash{Symbol => String}, nil] `{field:,value:}` if identifier is a percent selector, else `nil`
|
|
@@ -219,6 +241,8 @@ module Aspera
|
|
|
219
241
|
attr_accessor :ask_missing_mandatory, :ask_missing_optional
|
|
220
242
|
attr_writer :fail_on_missing_mandatory
|
|
221
243
|
|
|
244
|
+
# @param program_name [String] Name of the program
|
|
245
|
+
# @param argv [Array<String>, nil] Command line arguments to parse
|
|
222
246
|
def initialize(program_name, argv = nil)
|
|
223
247
|
# command line values *not* starting with '-'
|
|
224
248
|
@unprocessed_cmd_line_arguments = []
|
|
@@ -226,7 +250,8 @@ module Aspera
|
|
|
226
250
|
@unprocessed_cmd_line_options = []
|
|
227
251
|
# a copy of all initial options
|
|
228
252
|
@initial_cli_options = []
|
|
229
|
-
#
|
|
253
|
+
# Option descriptions: maps option symbol to its OptionValue descriptor
|
|
254
|
+
# @type [Hash{Symbol => OptionValue}]
|
|
230
255
|
@declared_options = {}
|
|
231
256
|
# do we ask missing options and arguments to user ?
|
|
232
257
|
@ask_missing_mandatory = false # STDIN.isatty
|
|
@@ -245,7 +270,7 @@ module Aspera
|
|
|
245
270
|
# options can also be provided by env vars : --param-name -> ASCLI_PARAM_NAME
|
|
246
271
|
env_prefix = program_name.upcase + OPTION_SEP_SYMBOL
|
|
247
272
|
ENV.each do |k, v|
|
|
248
|
-
@option_pairs_env[k
|
|
273
|
+
@option_pairs_env[k.delete_prefix(env_prefix).downcase.to_sym] = v if k.start_with?(env_prefix)
|
|
249
274
|
end
|
|
250
275
|
Log.log.debug{"env=#{@option_pairs_env}".red}
|
|
251
276
|
@unprocessed_cmd_line_options = []
|
|
@@ -276,6 +301,14 @@ module Aspera
|
|
|
276
301
|
# do not parse options yet, let's wait for option `-h` to be overridden
|
|
277
302
|
end
|
|
278
303
|
|
|
304
|
+
# Add a type to the message if not special types
|
|
305
|
+
# @param types [Array<Class>] types to add
|
|
306
|
+
# @return [String] Types if relevant
|
|
307
|
+
def add_types_info(types)
|
|
308
|
+
return '' if !types || types.empty? || types.eql?(Allowed::TYPES_ENUM) || types.eql?(Allowed::TYPES_BOOLEAN) || types.eql?(Allowed::TYPES_STRING)
|
|
309
|
+
" (#{types.map(&:name).join(', ')})"
|
|
310
|
+
end
|
|
311
|
+
|
|
279
312
|
# Declare an option
|
|
280
313
|
# @param option_symbol [Symbol] option name
|
|
281
314
|
# @param description [String] description for help
|
|
@@ -284,24 +317,26 @@ module Aspera
|
|
|
284
317
|
# @param default [Object] default value
|
|
285
318
|
# @param handler [Hash] handler for option value: keys: :o(object) and :m(method)
|
|
286
319
|
# @param deprecation [String] deprecation
|
|
320
|
+
# @param schema [String] Definition of schema for Hash parameters
|
|
287
321
|
# @param block [Proc] Block to execute when option is found
|
|
288
|
-
def declare(option_symbol, description, short: nil, allowed: nil, default: nil, handler: nil, deprecation: nil, &block)
|
|
322
|
+
def declare(option_symbol, description, short: nil, allowed: nil, default: nil, handler: nil, deprecation: nil, schema: nil, &block)
|
|
289
323
|
Aspera.assert_type(option_symbol, Symbol)
|
|
290
324
|
Aspera.assert(!@declared_options.key?(option_symbol)){"#{option_symbol} already declared"}
|
|
291
325
|
Aspera.assert(description[-1] != '.'){"#{option_symbol} ends with dot"}
|
|
292
326
|
Aspera.assert(description[0] == description[0].upcase){"#{option_symbol} description does not start with an uppercase"}
|
|
293
327
|
Aspera.assert(!['hash', 'extended value'].any?{ |s| description.downcase.include?(s)}){"#{option_symbol} shall use :allowed instead of hash/extended value in option description"}
|
|
294
328
|
Aspera.assert_type(handler, Hash) if handler
|
|
295
|
-
Aspera.assert(handler.keys.sort.eql?(%i[m o])) if handler
|
|
329
|
+
Aspera.assert(handler.keys.sort.eql?(%i[m o]), 'handler must have keys :m and :o') if handler
|
|
296
330
|
option_attrs = @declared_options[option_symbol] = OptionValue.new(
|
|
297
331
|
option: option_symbol,
|
|
298
332
|
description: description,
|
|
299
333
|
allowed: allowed,
|
|
300
334
|
handler: handler,
|
|
301
|
-
deprecation: deprecation
|
|
335
|
+
deprecation: deprecation,
|
|
336
|
+
schema: schema
|
|
302
337
|
)
|
|
303
338
|
real_types = option_attrs.types&.reject{ |i| [NilClass, String, Symbol].include?(i)}
|
|
304
|
-
description
|
|
339
|
+
description += add_types_info(real_types)
|
|
305
340
|
description = "#{description} (#{'deprecated'.blue}: #{deprecation})" if deprecation
|
|
306
341
|
set_option(option_symbol, default, where: 'default') unless default.nil?
|
|
307
342
|
on_args = [description]
|
|
@@ -340,32 +375,36 @@ module Aspera
|
|
|
340
375
|
end
|
|
341
376
|
|
|
342
377
|
# @param descr [String] description for help
|
|
343
|
-
# @param mandatory [Boolean]
|
|
344
|
-
# @param multiple [Boolean]
|
|
345
|
-
# @param accept_list [Array
|
|
378
|
+
# @param mandatory [Boolean] `true`: raise error no more argument
|
|
379
|
+
# @param multiple [Boolean] `true`: return all remaining arguments (Array). String: until marker
|
|
380
|
+
# @param accept_list [Array<Symbol>, NilClass] list of allowed values
|
|
346
381
|
# @param validation [Class, Array, NilClass] Accepted value type(s) or list of Symbols
|
|
347
382
|
# @param aliases [Hash] map of aliases: key = alias, value = real value
|
|
348
383
|
# @param default [Object] default value
|
|
349
384
|
# @return one value, list or nil (if optional and no default)
|
|
350
|
-
def get_next_argument(descr, mandatory: true, multiple: false, accept_list: nil, validation: Allowed::TYPES_STRING, aliases: nil, default: nil)
|
|
385
|
+
def get_next_argument(descr, mandatory: true, multiple: false, accept_list: nil, validation: Allowed::TYPES_STRING, aliases: nil, default: nil, schema: nil)
|
|
351
386
|
Aspera.assert_array_all(accept_list, Symbol) unless accept_list.nil?
|
|
352
387
|
Aspera.assert_hash_all(aliases, Symbol, Symbol) unless aliases.nil?
|
|
353
388
|
validation = Symbol unless accept_list.nil?
|
|
354
389
|
validation = [validation] unless validation.is_a?(Array) || validation.nil?
|
|
355
390
|
Aspera.assert_array_all(validation, Class){'validation'} unless validation.nil?
|
|
356
|
-
descr = "#{descr}
|
|
391
|
+
descr = "#{descr}#{add_types_info(validation)}"
|
|
357
392
|
result =
|
|
358
393
|
if !@unprocessed_cmd_line_arguments.empty?
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
394
|
+
case multiple
|
|
395
|
+
when true
|
|
396
|
+
values = @unprocessed_cmd_line_arguments.shift(@unprocessed_cmd_line_arguments.length)
|
|
397
|
+
when false
|
|
398
|
+
values = [@unprocessed_cmd_line_arguments.shift]
|
|
399
|
+
when String
|
|
400
|
+
index = @unprocessed_cmd_line_arguments.index(multiple)
|
|
401
|
+
if index
|
|
364
402
|
values = @unprocessed_cmd_line_arguments.shift(index)
|
|
365
|
-
@unprocessed_cmd_line_arguments.shift # remove
|
|
403
|
+
@unprocessed_cmd_line_arguments.shift # remove end marker
|
|
404
|
+
else
|
|
405
|
+
values = @unprocessed_cmd_line_arguments.shift(@unprocessed_cmd_line_arguments.length)
|
|
366
406
|
end
|
|
367
|
-
else
|
|
368
|
-
values = [@unprocessed_cmd_line_arguments.shift]
|
|
407
|
+
else Aspera.error_unexpected_value(multiple){'multiple'}
|
|
369
408
|
end
|
|
370
409
|
values = values.map{ |v| ExtendedValue.instance.evaluate(v, context: "argument: #{descr}", allowed: validation)}
|
|
371
410
|
# If expecting list and only one arg of type array : it is the list
|
|
@@ -378,7 +417,7 @@ module Aspera
|
|
|
378
417
|
multiple ? values : values.first
|
|
379
418
|
elsif !default.nil? then default
|
|
380
419
|
# no value provided, either get value interactively, or exception
|
|
381
|
-
elsif mandatory then get_interactive(descr, multiple: multiple, accept_list: accept_list)
|
|
420
|
+
elsif mandatory then get_interactive(descr, multiple: multiple, accept_list: accept_list, schema: schema)
|
|
382
421
|
end
|
|
383
422
|
if result.is_a?(String) && validation&.eql?(Allowed::TYPES_INTEGER)
|
|
384
423
|
int_result = Integer(result, exception: false)
|
|
@@ -392,18 +431,22 @@ module Aspera
|
|
|
392
431
|
if validation && (mandatory || !result.nil?)
|
|
393
432
|
value_list = multiple ? result : [result]
|
|
394
433
|
value_list.each do |value|
|
|
434
|
+
raise SchemaRequest.new(:argument, descr, schema) if validation.include?(Hash) && value.eql?(HELP)
|
|
395
435
|
raise Cli::BadArgument,
|
|
396
436
|
"Argument #{descr} is a #{value.class} but must be #{'one of: ' if validation.length > 1}#{validation.map(&:name).join(', ')}" unless validation.any?{ |t| value.is_a?(t)}
|
|
397
437
|
end
|
|
398
438
|
end
|
|
399
|
-
|
|
439
|
+
result
|
|
400
440
|
end
|
|
401
441
|
|
|
402
442
|
# Resource identifier as positional parameter
|
|
403
443
|
#
|
|
404
444
|
# @param description [String] description of the identifier
|
|
405
445
|
# @param block [Proc] block to search for identifier based on attribute value
|
|
406
|
-
# @return [String, Array] identifier or list of
|
|
446
|
+
# @return [String, Array<String>] identifier or list of IDs (if `bulk` option is set)
|
|
447
|
+
# @yieldparam field [String] The field name from percent selector
|
|
448
|
+
# @yieldparam value [String] The value from percent selector
|
|
449
|
+
# @yieldreturn [String] Resolved identifier
|
|
407
450
|
def instance_identifier(description: 'identifier', &block)
|
|
408
451
|
res_id = get_next_argument(description, multiple: get_option(:bulk)) if res_id.nil?
|
|
409
452
|
# Can be an Array
|
|
@@ -411,19 +454,28 @@ module Aspera
|
|
|
411
454
|
Aspera.assert(block_given?, type: Cli::BadArgument){"Percent syntax for #{description} not supported in this context"}
|
|
412
455
|
res_id = yield(m[:field], m[:value])
|
|
413
456
|
end
|
|
414
|
-
|
|
457
|
+
res_id
|
|
415
458
|
end
|
|
416
459
|
|
|
417
|
-
def get_next_command(command_list, aliases: nil);
|
|
460
|
+
def get_next_command(command_list, aliases: nil); get_next_argument('command', accept_list: command_list, aliases: aliases); end
|
|
461
|
+
|
|
462
|
+
# Get an option definition by name
|
|
463
|
+
# @param option_symbol [Symbol]
|
|
464
|
+
# @return [OptionValue] Option definition
|
|
465
|
+
# @raise [Cli::BadArgument] if option not found
|
|
466
|
+
def option_def(option_symbol)
|
|
467
|
+
Aspera.assert(@declared_options.key?(option_symbol), type: Cli::BadArgument){"Unknown option: #{option_symbol}"}
|
|
468
|
+
@declared_options[option_symbol]
|
|
469
|
+
end
|
|
418
470
|
|
|
419
471
|
# Get an option value by name
|
|
420
472
|
# either return value or calls handler, can return nil
|
|
421
473
|
# ask interactively if requested/required
|
|
474
|
+
# @param option_symbol [Symbol]
|
|
422
475
|
# @param mandatory [Boolean] if true, raise error if option not set
|
|
423
476
|
def get_option(option_symbol, mandatory: false)
|
|
424
477
|
Aspera.assert_type(option_symbol, Symbol)
|
|
425
|
-
|
|
426
|
-
option_attrs = @declared_options[option_symbol]
|
|
478
|
+
option_attrs = option_def(option_symbol)
|
|
427
479
|
result = option_attrs.value
|
|
428
480
|
# Do not fail for manual generation if option mandatory but not set
|
|
429
481
|
return :skip_missing_mandatory if result.nil? && mandatory && !@fail_on_missing_mandatory
|
|
@@ -432,11 +484,11 @@ module Aspera
|
|
|
432
484
|
Aspera.assert(!mandatory, type: Cli::BadArgument){"Missing mandatory option: #{option_symbol}"}
|
|
433
485
|
elsif @ask_missing_optional || mandatory
|
|
434
486
|
# ask_missing_mandatory
|
|
435
|
-
result = get_interactive(option_symbol.to_s, check_option: true, accept_list: option_attrs.values)
|
|
487
|
+
result = get_interactive(option_symbol.to_s, check_option: true, accept_list: option_attrs.values, schema: option_attrs.schema)
|
|
436
488
|
set_option(option_symbol, result, where: 'interactive')
|
|
437
489
|
end
|
|
438
490
|
end
|
|
439
|
-
|
|
491
|
+
result
|
|
440
492
|
end
|
|
441
493
|
|
|
442
494
|
# Set an option value by name, either store value or call handler
|
|
@@ -446,15 +498,15 @@ module Aspera
|
|
|
446
498
|
# @param where [String] Where the value comes from
|
|
447
499
|
def set_option(option_symbol, value, where: 'code override')
|
|
448
500
|
Aspera.assert_type(option_symbol, Symbol)
|
|
449
|
-
|
|
450
|
-
|
|
501
|
+
option = option_def(option_symbol)
|
|
502
|
+
raise SchemaRequest.new(:option, option.option, option.schema) if option.types&.include?(Hash) && value.eql?(HELP)
|
|
503
|
+
option.assign_value(value, where: where)
|
|
451
504
|
end
|
|
452
505
|
|
|
453
506
|
# Set option to `nil`
|
|
454
507
|
def clear_option(option_symbol)
|
|
455
508
|
Aspera.assert_type(option_symbol, Symbol)
|
|
456
|
-
|
|
457
|
-
@declared_options[option_symbol].clear
|
|
509
|
+
option_def(option_symbol).clear
|
|
458
510
|
end
|
|
459
511
|
|
|
460
512
|
# Adds each of the keys of specified hash as an option
|
|
@@ -477,7 +529,7 @@ module Aspera
|
|
|
477
529
|
|
|
478
530
|
# Check if there were unprocessed values to generate error
|
|
479
531
|
def command_or_arg_empty?
|
|
480
|
-
|
|
532
|
+
@unprocessed_cmd_line_arguments.empty?
|
|
481
533
|
end
|
|
482
534
|
|
|
483
535
|
# Unprocessed options or arguments ?
|
|
@@ -485,7 +537,7 @@ module Aspera
|
|
|
485
537
|
result = []
|
|
486
538
|
result.push("unprocessed options: #{@unprocessed_cmd_line_options}") unless @unprocessed_cmd_line_options.empty?
|
|
487
539
|
result.push("unprocessed values: #{@unprocessed_cmd_line_arguments}") unless @unprocessed_cmd_line_arguments.empty?
|
|
488
|
-
|
|
540
|
+
result
|
|
489
541
|
end
|
|
490
542
|
|
|
491
543
|
# Get all original options on command line used to generate a config in config file
|
|
@@ -495,7 +547,7 @@ module Aspera
|
|
|
495
547
|
@initial_cli_options.each do |option_argument|
|
|
496
548
|
# ignore short options
|
|
497
549
|
next unless option_argument.start_with?(OPTION_PREFIX)
|
|
498
|
-
name, value = option_argument
|
|
550
|
+
name, value = option_argument.delete_prefix(OPTION_PREFIX).split(OPTION_VALUE_SEPARATOR, 2)
|
|
499
551
|
# ignore options without value
|
|
500
552
|
next if value.nil?
|
|
501
553
|
Log.log.debug{"option #{name}=#{value}"}
|
|
@@ -504,7 +556,7 @@ module Aspera
|
|
|
504
556
|
DotContainer.dotted_to_container(path, smart_convert(value), result)
|
|
505
557
|
@unprocessed_cmd_line_options.delete(option_argument)
|
|
506
558
|
end
|
|
507
|
-
|
|
559
|
+
result
|
|
508
560
|
end
|
|
509
561
|
|
|
510
562
|
# @param only_defined [Boolean] if true, only return options that were defined
|
|
@@ -517,7 +569,7 @@ module Aspera
|
|
|
517
569
|
rescue => e
|
|
518
570
|
result[option_symbol] = e.to_s
|
|
519
571
|
end
|
|
520
|
-
|
|
572
|
+
result
|
|
521
573
|
end
|
|
522
574
|
|
|
523
575
|
# Removes already known options from the list
|
|
@@ -539,7 +591,7 @@ module Aspera
|
|
|
539
591
|
Log.log.trace1{"InvalidOption #{e}".red}
|
|
540
592
|
# An option like --a.b.c=d does: a={"b":{"c":ext_val(d)}}
|
|
541
593
|
if e.args.first.start_with?(OPTION_PREFIX)
|
|
542
|
-
name, value = e.args.first
|
|
594
|
+
name, value = e.args.first.delete_prefix(OPTION_PREFIX).split(OPTION_VALUE_SEPARATOR, 2)
|
|
543
595
|
if !value.nil?
|
|
544
596
|
path = name.split(DotContainer::SEPARATOR)
|
|
545
597
|
option_sym = self.class.option_line_to_name(path.shift).to_sym
|
|
@@ -565,7 +617,7 @@ module Aspera
|
|
|
565
617
|
print("#{prompt}> ")
|
|
566
618
|
line = $stdin.gets
|
|
567
619
|
Aspera.assert_type(line, String){'Unexpected end of standard input'}
|
|
568
|
-
|
|
620
|
+
line.chomp
|
|
569
621
|
end
|
|
570
622
|
|
|
571
623
|
# prompt user for input in a list of symbols
|
|
@@ -586,20 +638,19 @@ module Aspera
|
|
|
586
638
|
# Prompt user for input in a list of symbols
|
|
587
639
|
# @param descr [String] description for help
|
|
588
640
|
# @param check_option [Boolean] Check attributes of option with name=descr
|
|
589
|
-
# @param multiple [Boolean] true if multiple values expected
|
|
590
|
-
# @param accept_list [Array]
|
|
591
|
-
|
|
641
|
+
# @param multiple [Boolean, String] `true` if multiple values expected
|
|
642
|
+
# @param accept_list [Array<Symbol>,NilClass] List of expected values
|
|
643
|
+
# @return [String] user input
|
|
644
|
+
def get_interactive(descr, check_option: false, multiple: false, accept_list: nil, schema: nil)
|
|
592
645
|
option_attrs = @declared_options[descr.to_sym]
|
|
593
646
|
what = option_attrs ? 'option' : 'argument'
|
|
647
|
+
default_prompt = "#{what}: #{descr}"
|
|
594
648
|
if !@ask_missing_mandatory
|
|
595
|
-
message = "
|
|
596
|
-
if accept_list
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
Aspera.assert(false, self.class.multi_choice_assert_msg(message, accept_list), type: Cli::MissingArgument)
|
|
600
|
-
end
|
|
649
|
+
message = "Missing #{default_prompt}"
|
|
650
|
+
message = self.class.multi_choice_assert_msg(message, accept_list) if accept_list
|
|
651
|
+
message += "\n#{TerminalFormatter::HINT}Give `#{HELP}` as argument to retrieve the schema of the missing argument." if schema
|
|
652
|
+
raise Cli::MissingArgument, message
|
|
601
653
|
end
|
|
602
|
-
default_prompt = "#{what}: #{descr}"
|
|
603
654
|
# ask interactively
|
|
604
655
|
result = []
|
|
605
656
|
puts(' (one per line, end with empty line)') if multiple
|
|
@@ -613,18 +664,20 @@ module Aspera
|
|
|
613
664
|
return entry unless multiple
|
|
614
665
|
result.push(entry)
|
|
615
666
|
end
|
|
616
|
-
|
|
667
|
+
result
|
|
617
668
|
end
|
|
618
669
|
|
|
619
|
-
# Read remaining args and build an Array or Hash
|
|
620
|
-
# @param value [
|
|
621
|
-
|
|
670
|
+
# Read remaining args and build an `Array` or `Hash`
|
|
671
|
+
# @param value [String] Argument to `@:` extended value
|
|
672
|
+
# @return [Hash, Array] Object representing dot-path values
|
|
673
|
+
def args_as_extended(end_marker)
|
|
622
674
|
# This extended value does not take args (`@:`)
|
|
623
|
-
ExtendedValue.assert_no_value(
|
|
675
|
+
# ExtendedValue.assert_no_value(end_marker, :p)
|
|
676
|
+
end_marker = SpecialValues::EOA if end_marker.empty?
|
|
624
677
|
result = nil
|
|
625
|
-
get_next_argument(
|
|
626
|
-
Aspera.assert(
|
|
627
|
-
path, value =
|
|
678
|
+
get_next_argument('args', multiple: end_marker).each do |argument|
|
|
679
|
+
Aspera.assert(argument.include?(OPTION_VALUE_SEPARATOR)){"Positional argument: #{argument} does not include #{OPTION_VALUE_SEPARATOR}"}
|
|
680
|
+
path, value = argument.split(OPTION_VALUE_SEPARATOR, 2)
|
|
628
681
|
result = DotContainer.dotted_to_container(path.split(DotContainer::SEPARATOR), smart_convert(value), result)
|
|
629
682
|
end
|
|
630
683
|
result
|
|
@@ -651,7 +704,7 @@ module Aspera
|
|
|
651
704
|
def symbol_to_option(symbol, opt_val = nil)
|
|
652
705
|
result = [OPTION_PREFIX, symbol.to_s.gsub(OPTION_SEP_SYMBOL, OPTION_SEP_LINE)].join
|
|
653
706
|
result = [result, OPTION_VALUE_SEPARATOR, opt_val].join unless opt_val.nil?
|
|
654
|
-
|
|
707
|
+
result
|
|
655
708
|
end
|
|
656
709
|
|
|
657
710
|
# TODO: use formatter
|
|
@@ -704,6 +757,8 @@ module Aspera
|
|
|
704
757
|
SOURCE_USER = 'cmdline' # cspell:disable-line
|
|
705
758
|
# Percent selector: select by this field for this value
|
|
706
759
|
REGEX_LOOKUP_ID_BY_FIELD = /^%([^:]+):(.*)$/
|
|
760
|
+
# Ask for schema of Extended value
|
|
761
|
+
HELP = 'help'
|
|
707
762
|
|
|
708
763
|
private_constant :OPTION_SEP_LINE, :OPTION_SEP_SYMBOL, :OPTION_VALUE_SEPARATOR, :OPTION_PREFIX, :OPTIONS_STOP, :SOURCE_USER, :REGEX_LOOKUP_ID_BY_FIELD
|
|
709
764
|
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
openapi: 3.0.0
|
|
2
|
+
info:
|
|
3
|
+
title: Aspera CLI Options Schema
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
description: Schema definitions for Aspera CLI options
|
|
6
|
+
paths: {}
|
|
7
|
+
components:
|
|
8
|
+
schemas:
|
|
9
|
+
TransferInfo:
|
|
10
|
+
type: object
|
|
11
|
+
description: Optional parameters to control multi-session transfers, Web Socket Session, resume policy, and additional arguments for `ascp`.
|
|
12
|
+
properties:
|
|
13
|
+
wss:
|
|
14
|
+
type: boolean
|
|
15
|
+
description: Enable Web Socket Session when available.
|
|
16
|
+
default: true
|
|
17
|
+
quiet:
|
|
18
|
+
type: boolean
|
|
19
|
+
description: Suppress the `ascp` progress bar display.
|
|
20
|
+
default: false
|
|
21
|
+
trusted_certs:
|
|
22
|
+
type: array
|
|
23
|
+
description: List of trusted certificate repositories.
|
|
24
|
+
items:
|
|
25
|
+
type: string
|
|
26
|
+
client_ssh_key:
|
|
27
|
+
type: string
|
|
28
|
+
description: SSH key type to use for token-based transfers.
|
|
29
|
+
enum:
|
|
30
|
+
- rsa
|
|
31
|
+
- dsa_rsa
|
|
32
|
+
- per_client
|
|
33
|
+
default: rsa
|
|
34
|
+
ascp_args:
|
|
35
|
+
type: array
|
|
36
|
+
description: List of native `ascp` command-line arguments.
|
|
37
|
+
items:
|
|
38
|
+
type: string
|
|
39
|
+
default: []
|
|
40
|
+
spawn_timeout_sec:
|
|
41
|
+
type: number
|
|
42
|
+
format: float
|
|
43
|
+
description: Multi session - Maximum time (in seconds) to verify that `ascp` is running.
|
|
44
|
+
default: 3
|
|
45
|
+
spawn_delay_sec:
|
|
46
|
+
type: number
|
|
47
|
+
format: float
|
|
48
|
+
description: Multi session - Delay (in seconds) between starting each `ascp` session.
|
|
49
|
+
default: 2
|
|
50
|
+
multi_incr_udp:
|
|
51
|
+
type: boolean
|
|
52
|
+
description: >-
|
|
53
|
+
Multi session - Increment UDP port for each session.
|
|
54
|
+
|
|
55
|
+
If `true`, each session uses a different UDP port starting at `fasp_port` (default: 33001).
|
|
56
|
+
|
|
57
|
+
If `false`, all sessions use the same `fasp_port` (or `ascp` default).
|
|
58
|
+
default: true
|
|
59
|
+
resume:
|
|
60
|
+
type: object
|
|
61
|
+
description: Configuration for automatic transfer resume on interruption.
|
|
62
|
+
properties:
|
|
63
|
+
iter_max:
|
|
64
|
+
type: integer
|
|
65
|
+
description: Maximum number of retry attempts on error.
|
|
66
|
+
default: 7
|
|
67
|
+
sleep_initial:
|
|
68
|
+
type: integer
|
|
69
|
+
description: Initial sleep duration (in seconds) before first retry.
|
|
70
|
+
default: 2
|
|
71
|
+
sleep_factor:
|
|
72
|
+
type: integer
|
|
73
|
+
description: Multiplier applied to sleep duration between consecutive retry attempts.
|
|
74
|
+
default: 2
|
|
75
|
+
sleep_max:
|
|
76
|
+
type: integer
|
|
77
|
+
description: Maximum sleep duration (in seconds) between retry attempts.
|
|
78
|
+
default: 60
|
|
79
|
+
monitor:
|
|
80
|
+
type: boolean
|
|
81
|
+
description: Enable use of the `ascp` management port for transfer monitoring.
|
|
82
|
+
default: true
|
|
@@ -23,12 +23,12 @@ module Aspera
|
|
|
23
23
|
rescue StandardError => e
|
|
24
24
|
nagios.add_critical('api', e.to_s)
|
|
25
25
|
end
|
|
26
|
-
|
|
26
|
+
Result::ObjectList.new(nagios.status_list)
|
|
27
27
|
when :entitlement
|
|
28
28
|
entitlement_id = options.get_option(:username, mandatory: true)
|
|
29
29
|
customer_id = options.get_option(:password, mandatory: true)
|
|
30
30
|
api_metering = Api::Alee.new(entitlement_id, customer_id)
|
|
31
|
-
return
|
|
31
|
+
return Result::SingleObject.new(api_metering.read('entitlement'))
|
|
32
32
|
end
|
|
33
33
|
end
|
|
34
34
|
end
|