aspera-cli 4.17.0 → 4.18.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 +2 -4
- data/CHANGELOG.md +23 -0
- data/CONTRIBUTING.md +15 -1
- data/README.md +620 -378
- data/bin/ascli +5 -0
- data/bin/asession +2 -2
- data/lib/aspera/agent/alpha.rb +6 -4
- data/lib/aspera/agent/base.rb +9 -6
- data/lib/aspera/agent/connect.rb +4 -4
- data/lib/aspera/agent/direct.rb +56 -37
- data/lib/aspera/agent/httpgw.rb +23 -324
- data/lib/aspera/agent/node.rb +19 -20
- data/lib/aspera/agent/trsdk.rb +19 -20
- data/lib/aspera/api/aoc.rb +17 -14
- data/lib/aspera/api/cos_node.rb +4 -4
- data/lib/aspera/api/httpgw.rb +339 -0
- data/lib/aspera/api/node.rb +34 -21
- data/lib/aspera/ascmd.rb +4 -3
- data/lib/aspera/ascp/installation.rb +15 -7
- data/lib/aspera/ascp/management.rb +2 -2
- data/lib/aspera/cli/basic_auth_plugin.rb +5 -9
- data/lib/aspera/cli/extended_value.rb +12 -6
- data/lib/aspera/cli/formatter.rb +155 -65
- data/lib/aspera/cli/hints.rb +18 -0
- data/lib/aspera/cli/main.rb +22 -29
- data/lib/aspera/cli/manager.rb +53 -36
- data/lib/aspera/cli/plugin.rb +26 -17
- data/lib/aspera/cli/plugin_factory.rb +31 -20
- data/lib/aspera/cli/plugins/alee.rb +14 -2
- data/lib/aspera/cli/plugins/aoc.rb +141 -131
- data/lib/aspera/cli/plugins/ats.rb +1 -1
- data/lib/aspera/cli/plugins/config.rb +52 -46
- data/lib/aspera/cli/plugins/console.rb +8 -5
- data/lib/aspera/cli/plugins/faspex.rb +27 -19
- data/lib/aspera/cli/plugins/faspex5.rb +222 -149
- data/lib/aspera/cli/plugins/faspio.rb +85 -0
- data/lib/aspera/cli/plugins/httpgw.rb +55 -0
- data/lib/aspera/cli/plugins/node.rb +86 -29
- data/lib/aspera/cli/plugins/orchestrator.rb +31 -29
- data/lib/aspera/cli/plugins/preview.rb +6 -2
- data/lib/aspera/cli/plugins/server.rb +5 -5
- data/lib/aspera/cli/plugins/shares.rb +16 -14
- data/lib/aspera/cli/sync_actions.rb +6 -6
- data/lib/aspera/cli/transfer_agent.rb +5 -4
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/environment.rb +7 -6
- data/lib/aspera/faspex_gw.rb +5 -4
- data/lib/aspera/faspex_postproc.rb +2 -2
- data/lib/aspera/log.rb +6 -3
- data/lib/aspera/node_simulator.rb +2 -2
- data/lib/aspera/oauth/base.rb +31 -19
- data/lib/aspera/oauth/factory.rb +12 -13
- data/lib/aspera/oauth/generic.rb +1 -0
- data/lib/aspera/oauth/jwt.rb +18 -15
- data/lib/aspera/oauth/url_json.rb +8 -6
- data/lib/aspera/open_application.rb +5 -7
- data/lib/aspera/persistency_folder.rb +2 -2
- data/lib/aspera/preview/generator.rb +3 -3
- data/lib/aspera/preview/options.rb +3 -3
- data/lib/aspera/preview/terminal.rb +4 -4
- data/lib/aspera/preview/utils.rb +3 -3
- data/lib/aspera/proxy_auto_config.rb +5 -1
- data/lib/aspera/rest.rb +60 -74
- data/lib/aspera/rest_call_error.rb +1 -1
- data/lib/aspera/rest_error_analyzer.rb +2 -2
- data/lib/aspera/rest_errors_aspera.rb +1 -1
- data/lib/aspera/resumer.rb +1 -1
- data/lib/aspera/secret_hider.rb +2 -4
- data/lib/aspera/ssh.rb +1 -1
- data/lib/aspera/transfer/parameters.rb +39 -36
- data/lib/aspera/transfer/spec.rb +2 -0
- data/lib/aspera/transfer/sync.rb +2 -1
- data/lib/aspera/transfer/uri.rb +1 -1
- data/lib/aspera/uri_reader.rb +5 -4
- data/lib/aspera/web_auth.rb +1 -1
- data/lib/aspera/web_server_simple.rb +4 -3
- data.tar.gz.sig +0 -0
- metadata +5 -3
- metadata.gz.sig +0 -0
- data/lib/aspera/cli/plugins/bss.rb +0 -71
data/lib/aspera/cli/formatter.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# cspell:ignore jsonpp
|
4
|
+
require 'aspera/preview/terminal'
|
4
5
|
require 'aspera/secret_hider'
|
5
6
|
require 'aspera/environment'
|
6
7
|
require 'aspera/log'
|
7
8
|
require 'aspera/assert'
|
8
9
|
require 'terminal-table'
|
10
|
+
require 'tty-spinner'
|
9
11
|
require 'yaml'
|
10
12
|
require 'pp'
|
11
13
|
|
@@ -14,8 +16,9 @@ module Aspera
|
|
14
16
|
CONF_OVERVIEW_KEYS = %w[preset parameter value].freeze
|
15
17
|
# This class is used to transform a complex structure into a simple hash
|
16
18
|
class Flattener
|
17
|
-
def initialize
|
19
|
+
def initialize(formatter)
|
18
20
|
@result = nil
|
21
|
+
@formatter = formatter
|
19
22
|
end
|
20
23
|
|
21
24
|
# General method
|
@@ -48,9 +51,9 @@ module Aspera
|
|
48
51
|
elsif something.is_a?(Array)
|
49
52
|
flatten_array(something, name)
|
50
53
|
elsif something.is_a?(String) && something.empty?
|
51
|
-
@result[name] =
|
54
|
+
@result[name] = @formatter.special_format('empty string')
|
52
55
|
elsif something.nil?
|
53
|
-
@result[name] =
|
56
|
+
@result[name] = @formatter.special_format('null')
|
54
57
|
# elsif something.eql?(true) || something.eql?(false)
|
55
58
|
# @result[name] = something
|
56
59
|
else
|
@@ -63,7 +66,7 @@ module Aspera
|
|
63
66
|
# @param name [String] name of englobing key
|
64
67
|
def flatten_array(array, name)
|
65
68
|
if array.empty?
|
66
|
-
@result[name] =
|
69
|
+
@result[name] = @formatter.special_format('empty list')
|
67
70
|
elsif array.all?(String)
|
68
71
|
@result[name] = array.join("\n")
|
69
72
|
elsif array.all?{|i| i.is_a?(Hash) && i.keys.eql?(%w[name])}
|
@@ -85,18 +88,18 @@ module Aspera
|
|
85
88
|
flatten_any(v, "#{prefix}#{k}")
|
86
89
|
end
|
87
90
|
end
|
88
|
-
end
|
91
|
+
end
|
89
92
|
|
90
93
|
# Take care of output
|
91
94
|
class Formatter
|
95
|
+
# remove a fields from the list
|
92
96
|
FIELDS_LESS = '-'
|
93
97
|
CSV_RECORD_SEPARATOR = "\n"
|
94
98
|
CSV_FIELD_SEPARATOR = ','
|
95
99
|
# supported output formats
|
96
|
-
DISPLAY_FORMATS = %i[text nagios ruby json jsonpp yaml table csv].freeze
|
100
|
+
DISPLAY_FORMATS = %i[text nagios ruby json jsonpp yaml table csv image].freeze
|
97
101
|
# user output levels
|
98
102
|
DISPLAY_LEVELS = %i[info data error].freeze
|
99
|
-
RESULT_PARAMS = %i[type data total fields name].freeze
|
100
103
|
|
101
104
|
private_constant :DISPLAY_FORMATS, :DISPLAY_LEVELS, :CSV_RECORD_SEPARATOR, :CSV_FIELD_SEPARATOR
|
102
105
|
# prefix to display error messages in user messages (terminal)
|
@@ -105,19 +108,6 @@ module Aspera
|
|
105
108
|
HINT_FLASH = 'HINT:'.bg_green.gray.blink.freeze
|
106
109
|
|
107
110
|
class << self
|
108
|
-
# Highlight special values
|
109
|
-
def special(what, use_colors: $stdout.isatty)
|
110
|
-
result = $stdout.isatty ? "<#{what}>" : "<#{what}>"
|
111
|
-
if use_colors
|
112
|
-
result = if %w[null empty].any?{|s|what.include?(s)}
|
113
|
-
result.dim
|
114
|
-
else
|
115
|
-
result.reverse_color
|
116
|
-
end
|
117
|
-
end
|
118
|
-
return result
|
119
|
-
end
|
120
|
-
|
121
111
|
def all_but(list)
|
122
112
|
list = [list] unless list.is_a?(Array)
|
123
113
|
return list.map{|i|"#{FIELDS_LESS}#{i}"}.unshift(ExtendedValue::ALL)
|
@@ -125,7 +115,7 @@ module Aspera
|
|
125
115
|
|
126
116
|
def tick(yes)
|
127
117
|
result =
|
128
|
-
if Environment.
|
118
|
+
if Environment.terminal_supports_unicode?
|
129
119
|
if yes
|
130
120
|
"\u2713"
|
131
121
|
else
|
@@ -150,24 +140,55 @@ module Aspera
|
|
150
140
|
end
|
151
141
|
return result
|
152
142
|
end
|
153
|
-
end
|
143
|
+
end
|
154
144
|
|
155
145
|
# initialize the formatter
|
156
146
|
def initialize
|
157
147
|
@options = {}
|
148
|
+
@spinner = nil
|
149
|
+
end
|
150
|
+
|
151
|
+
# Highlight special values
|
152
|
+
def special_format(what, use_colors: $stdout.isatty)
|
153
|
+
result = $stdout.isatty ? "<#{what}>" : "<#{what}>"
|
154
|
+
if use_colors
|
155
|
+
result = if %w[null empty].any?{|s|what.include?(s)}
|
156
|
+
result.dim
|
157
|
+
else
|
158
|
+
result.reverse_color
|
159
|
+
end
|
160
|
+
end
|
161
|
+
return result
|
158
162
|
end
|
159
163
|
|
164
|
+
# call this after REST calls if several api calls are expected
|
165
|
+
def long_operation_running(title = '')
|
166
|
+
return unless Environment.terminal?
|
167
|
+
if @spinner.nil?
|
168
|
+
@spinner = TTY::Spinner.new('[:spinner] :title', format: :classic)
|
169
|
+
@spinner.start
|
170
|
+
end
|
171
|
+
@spinner.update(title: title)
|
172
|
+
@spinner.spin
|
173
|
+
end
|
174
|
+
|
175
|
+
# options are: format, output, display, fields, select, table_style, flat_hash, transpose_single
|
160
176
|
def option_handler(option_symbol, operation, value=nil)
|
161
177
|
Aspera.assert_values(operation, %i[set get])
|
162
178
|
case operation
|
163
179
|
when :set
|
164
180
|
@options[option_symbol] = value
|
165
|
-
|
181
|
+
case option_symbol
|
182
|
+
when :output
|
166
183
|
$stdout = if value.eql?('-')
|
167
184
|
STDOUT # rubocop:disable Style/GlobalStdStream
|
168
185
|
else
|
169
186
|
File.open(value, 'w')
|
170
187
|
end
|
188
|
+
when :image
|
189
|
+
allowed_options = Preview::Terminal.method(:build).parameters.select{|i|i[0].eql?(:key)}.map{|i|i[1]}
|
190
|
+
unknown_options = value.keys.map(&:to_sym) - allowed_options
|
191
|
+
raise "Invalid parameter(s) for option image: #{unknown_options.join(', ')}, use #{allowed_options.join(', ')}" unless unknown_options.empty?
|
171
192
|
end
|
172
193
|
when :get then return @options[option_symbol]
|
173
194
|
else Aspera.error_unreachable_line
|
@@ -176,6 +197,12 @@ module Aspera
|
|
176
197
|
end
|
177
198
|
|
178
199
|
def declare_options(options)
|
200
|
+
default_table_style = if Environment.terminal_supports_unicode?
|
201
|
+
{border: :unicode_round}
|
202
|
+
else
|
203
|
+
{}
|
204
|
+
end
|
205
|
+
|
179
206
|
options.declare(:format, 'Output format', values: DISPLAY_FORMATS, handler: {o: self, m: :option_handler}, default: :table)
|
180
207
|
options.declare(:output, 'Destination for results', types: String, handler: {o: self, m: :option_handler})
|
181
208
|
options.declare(:display, 'Output only some information', values: DISPLAY_LEVELS, handler: {o: self, m: :option_handler}, default: :info)
|
@@ -184,10 +211,11 @@ module Aspera
|
|
184
211
|
types: [String, Array, Regexp, Proc],
|
185
212
|
default: ExtendedValue::DEF)
|
186
213
|
options.declare(:select, 'Select only some items in lists: column, value', types: [Hash, Proc], handler: {o: self, m: :option_handler})
|
187
|
-
options.declare(:table_style, 'Table display style', handler: {o: self, m: :option_handler}, default:
|
214
|
+
options.declare(:table_style, 'Table display style', types: [Hash], handler: {o: self, m: :option_handler}, default: default_table_style)
|
188
215
|
options.declare(:flat_hash, 'Display deep values as additional keys', values: :bool, handler: {o: self, m: :option_handler}, default: true)
|
189
216
|
options.declare(:transpose_single, 'Single object fields output vertically', values: :bool, handler: {o: self, m: :option_handler}, default: true)
|
190
217
|
options.declare(:show_secrets, 'Show secrets on command output', values: :bool, handler: {o: self, m: :option_handler}, default: false)
|
218
|
+
options.declare(:image, 'Options for image display', types: Hash, handler: {o: self, m: :option_handler}, default: {})
|
191
219
|
end
|
192
220
|
|
193
221
|
# main output method
|
@@ -220,14 +248,15 @@ module Aspera
|
|
220
248
|
data.each_with_object({}){|v, m|v.each_key{|c|m[c] = true}}.keys
|
221
249
|
end
|
222
250
|
|
223
|
-
#
|
224
|
-
# data
|
225
|
-
# default
|
251
|
+
# @return the list of fields to display
|
252
|
+
# @param data [Array<Hash>] data to display
|
253
|
+
# @param default [Array<String>, Proc] list of fields to display by default (may contain special values)
|
226
254
|
def compute_fields(data, default)
|
227
255
|
Log.log.debug{"compute_fields: data:#{data.class} default:#{default.class} #{default}"}
|
256
|
+
# the requested list of fields, but if can contain special values
|
228
257
|
request =
|
229
258
|
case @options[:fields]
|
230
|
-
when NilClass then [ExtendedValue::DEF]
|
259
|
+
# when NilClass then [ExtendedValue::DEF]
|
231
260
|
when String then @options[:fields].split(',')
|
232
261
|
when Array then @options[:fields]
|
233
262
|
when Regexp then return all_fields(data).select{|i|i.match(@options[:fields])}
|
@@ -261,20 +290,39 @@ module Aspera
|
|
261
290
|
return result
|
262
291
|
end
|
263
292
|
|
293
|
+
# filter the list of items on the fields option
|
294
|
+
def filter_list_on_fields(data)
|
295
|
+
# by default, keep all data intact
|
296
|
+
return data if @options[:fields].eql?(ExtendedValue::DEF) && @options[:select].nil?
|
297
|
+
Aspera.assert_type(data, Array){'Filtering fields or select requires result is an Array of Hash'}
|
298
|
+
Aspera.assert(data.all?(Hash)){'Filtering fields or select requires result is an Array of Hash'}
|
299
|
+
filter_columns_on_select(data)
|
300
|
+
return data if @options[:fields].eql?(ExtendedValue::DEF)
|
301
|
+
selected_fields = compute_fields(data, @options[:fields])
|
302
|
+
return data.map{|i|i[selected_fields.first]} if selected_fields.length == 1
|
303
|
+
return data.map{|i|i.select{|k, _|selected_fields.include?(k)}}
|
304
|
+
end
|
305
|
+
|
306
|
+
# filter the list of items on the select option
|
307
|
+
# @param data [Array<Hash>] list of items
|
308
|
+
def filter_columns_on_select(data)
|
309
|
+
case @options[:select]
|
310
|
+
when Proc
|
311
|
+
data.select!{|i|@options[:select].call(i)}
|
312
|
+
when Hash
|
313
|
+
@options[:select].each{|k, v|data.select!{|i|i[k].eql?(v)}}
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
264
317
|
# this method displays a table
|
265
318
|
# object_array: array of hash
|
266
319
|
# fields: list of column names
|
267
320
|
def display_table(object_array, fields)
|
268
321
|
Aspera.assert(!fields.nil?){'missing fields parameter'}
|
269
|
-
|
270
|
-
when Proc
|
271
|
-
object_array.select!{|i|@options[:select].call(i)}
|
272
|
-
when Hash
|
273
|
-
@options[:select].each{|k, v|object_array.select!{|i|i[k].eql?(v)}}
|
274
|
-
end
|
322
|
+
filter_columns_on_select(object_array)
|
275
323
|
if object_array.empty?
|
276
324
|
# no display for csv
|
277
|
-
display_message(:info,
|
325
|
+
display_message(:info, special_format('empty')) if @options[:format].eql?(:table)
|
278
326
|
return
|
279
327
|
end
|
280
328
|
if object_array.length == 1 && fields.length == 1
|
@@ -294,72 +342,114 @@ module Aspera
|
|
294
342
|
# here : fields : list of column names
|
295
343
|
case @options[:format]
|
296
344
|
when :table
|
297
|
-
style = @options[:table_style].chars
|
298
345
|
# display the table !
|
299
346
|
display_message(:data, Terminal::Table.new(
|
300
347
|
headings: fields,
|
301
348
|
rows: final_table_rows,
|
302
|
-
|
303
|
-
border_y: style[1],
|
304
|
-
border_i: style[2]))
|
349
|
+
style: @options[:table_style]&.symbolize_keys))
|
305
350
|
when :csv
|
306
351
|
display_message(:data, final_table_rows.map{|t| t.join(CSV_FIELD_SEPARATOR)}.join(CSV_RECORD_SEPARATOR))
|
307
352
|
end
|
308
353
|
end
|
309
354
|
|
355
|
+
# @return text suitable to display an image from url
|
356
|
+
def status_image(blob)
|
357
|
+
begin
|
358
|
+
raise URI::InvalidURIError, 'not uri' if !(blob =~ /\A#{URI::DEFAULT_PARSER.make_regexp}\z/)
|
359
|
+
# it's a url
|
360
|
+
url = blob
|
361
|
+
unless OpenApplication.instance.url_method.eql?(:text)
|
362
|
+
OpenApplication.instance.uri(url)
|
363
|
+
return ''
|
364
|
+
end
|
365
|
+
# remote_image = Rest.new(base_url: url).read('')
|
366
|
+
# mime = remote_image[:http]['content-type']
|
367
|
+
# blob = remote_image[:http].body
|
368
|
+
# Log.log.warn("Image ? #{remote_image[:http]['content-type']}") unless mime.include?('image/')
|
369
|
+
blob = UriReader.read(url)
|
370
|
+
rescue URI::InvalidURIError
|
371
|
+
nil
|
372
|
+
end
|
373
|
+
# try base64
|
374
|
+
begin
|
375
|
+
blob = Base64.strict_decode64(blob)
|
376
|
+
rescue
|
377
|
+
nil
|
378
|
+
end
|
379
|
+
return Preview::Terminal.build(blob, **@options[:image].symbolize_keys)
|
380
|
+
end
|
381
|
+
|
310
382
|
# this method displays the results, especially the table format
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
383
|
+
# @param type [Symbol] type of data
|
384
|
+
# @param data [Object] data to display
|
385
|
+
# @param total [Integer] total number of items
|
386
|
+
# @param fields [Array<String>] list of fields to display
|
387
|
+
# @param name [String] name of the column to display
|
388
|
+
def display_results(type:, data: nil, total: nil, fields: nil, name: nil)
|
389
|
+
Log.log.debug{"display_results: #{type} class=#{data.class} data=#{data}"}
|
390
|
+
Aspera.assert_type(type, Symbol){'result must have type'}
|
391
|
+
Aspera.assert(!data.nil? || %i[empty nothing].include?(type)){'result must have data'}
|
392
|
+
display_item_count(data.length, total) unless total.nil?
|
393
|
+
SecretHider.deep_remove_secret(data) unless @options[:show_secrets] || @options[:display].eql?(:data)
|
319
394
|
case @options[:format]
|
320
395
|
when :text
|
321
|
-
display_message(:data,
|
396
|
+
display_message(:data, data.to_s)
|
322
397
|
when :nagios
|
323
|
-
Nagios.process(
|
398
|
+
Nagios.process(data)
|
324
399
|
when :ruby
|
325
|
-
display_message(:data, PP.pp(
|
400
|
+
display_message(:data, PP.pp(filter_list_on_fields(data), +''))
|
326
401
|
when :json
|
327
|
-
display_message(:data, JSON.generate(
|
402
|
+
display_message(:data, JSON.generate(filter_list_on_fields(data)))
|
328
403
|
when :jsonpp
|
329
|
-
display_message(:data, JSON.pretty_generate(
|
404
|
+
display_message(:data, JSON.pretty_generate(filter_list_on_fields(data)))
|
330
405
|
when :yaml
|
331
|
-
display_message(:data,
|
406
|
+
display_message(:data, YAML.dump(filter_list_on_fields(data)))
|
407
|
+
when :image
|
408
|
+
# assume it is an url
|
409
|
+
url = data
|
410
|
+
case type
|
411
|
+
when :single_object, :object_list
|
412
|
+
url = [url] if type.eql?(:single_object)
|
413
|
+
raise 'image display requires a single result' unless url.length == 1
|
414
|
+
fields = compute_fields(url, fields)
|
415
|
+
raise 'select a field to display' unless fields.length == 1
|
416
|
+
url = url.first
|
417
|
+
raise 'no such field' unless url.key?(fields.first)
|
418
|
+
url = url[fields.first]
|
419
|
+
end
|
420
|
+
raise "not url: #{url.class} #{url}" unless url.is_a?(String)
|
421
|
+
display_message(:data, status_image(url))
|
332
422
|
when :table, :csv
|
333
|
-
case
|
423
|
+
case type
|
334
424
|
when :config_over
|
335
|
-
display_table(Flattener.new.config_over(
|
425
|
+
display_table(Flattener.new(self).config_over(data), CONF_OVERVIEW_KEYS)
|
336
426
|
when :object_list, :single_object
|
337
|
-
obj_list =
|
338
|
-
obj_list = [obj_list] if
|
427
|
+
obj_list = data
|
428
|
+
obj_list = [obj_list] if type.eql?(:single_object)
|
339
429
|
Aspera.assert_type(obj_list, Array)
|
340
430
|
Aspera.assert(obj_list.all?(Hash)){"expecting Array of Hash: #{obj_list.inspect}"}
|
341
431
|
# :object_list is an array of hash tables, where key=colum name
|
342
|
-
obj_list = obj_list.map{|obj|Flattener.new.flatten(obj)} if @options[:flat_hash]
|
343
|
-
display_table(obj_list, compute_fields(obj_list,
|
432
|
+
obj_list = obj_list.map{|obj|Flattener.new(self).flatten(obj)} if @options[:flat_hash]
|
433
|
+
display_table(obj_list, compute_fields(obj_list, fields))
|
344
434
|
when :value_list
|
345
435
|
# :value_list is a simple array of values, name of column provided in the :name
|
346
|
-
display_table(
|
436
|
+
display_table(data.map { |i| { name => i } }, [name])
|
347
437
|
when :empty # no table
|
348
|
-
display_message(:info,
|
438
|
+
display_message(:info, special_format('empty'))
|
349
439
|
return
|
350
440
|
when :nothing # no result expected
|
351
441
|
Log.log.debug('no result expected')
|
352
442
|
when :status # no table
|
353
443
|
# :status displays a simple message
|
354
|
-
display_message(:info,
|
444
|
+
display_message(:info, data)
|
355
445
|
when :text # no table
|
356
446
|
# :status displays a simple message
|
357
|
-
display_message(:data,
|
447
|
+
display_message(:data, data)
|
358
448
|
when :other_struct # no table
|
359
449
|
# :other_struct is any other type of structure
|
360
|
-
display_message(:data, PP.pp(
|
450
|
+
display_message(:data, PP.pp(data, +''))
|
361
451
|
else
|
362
|
-
raise "unknown data type: #{
|
452
|
+
raise "unknown data type: #{type}"
|
363
453
|
end
|
364
454
|
end
|
365
455
|
end
|
data/lib/aspera/cli/hints.rb
CHANGED
@@ -47,6 +47,24 @@ module Aspera
|
|
47
47
|
'if you provide a path: prefix with @file:',
|
48
48
|
'e.g. --private-key=@file:/path/to/key.pem'
|
49
49
|
]
|
50
|
+
},
|
51
|
+
{
|
52
|
+
exception: RuntimeError,
|
53
|
+
match: /unexpected last event type: INIT/,
|
54
|
+
remediation: [
|
55
|
+
'ascp exited unexpectedly',
|
56
|
+
'it might be due to SSH handshake failure',
|
57
|
+
'one can check SSH connection using ssh command'
|
58
|
+
]
|
59
|
+
},
|
60
|
+
{
|
61
|
+
exception: RuntimeError,
|
62
|
+
match: /Wrong remote host:/,
|
63
|
+
remediation: [
|
64
|
+
'If remote node is Cloud Pak For Integration',
|
65
|
+
'Make sure that a LoadBalancer is active on cluster',
|
66
|
+
'Check the external address of Aspera tcp-proxy pod'
|
67
|
+
]
|
50
68
|
}
|
51
69
|
]
|
52
70
|
|
data/lib/aspera/cli/main.rb
CHANGED
@@ -9,7 +9,6 @@ require 'aspera/cli/transfer_agent'
|
|
9
9
|
require 'aspera/cli/version'
|
10
10
|
require 'aspera/cli/info'
|
11
11
|
require 'aspera/cli/hints'
|
12
|
-
require 'aspera/preview/terminal'
|
13
12
|
require 'aspera/secret_hider'
|
14
13
|
require 'aspera/log'
|
15
14
|
require 'aspera/assert'
|
@@ -20,9 +19,10 @@ module Aspera
|
|
20
19
|
class Main
|
21
20
|
# Plugins store transfer result using this key and use result_transfer_multiple()
|
22
21
|
STATUS_FIELD = 'status'
|
23
|
-
|
22
|
+
COMMAND_CONFIG = :config
|
23
|
+
COMMAND_HELP = :help
|
24
24
|
|
25
|
-
private_constant :
|
25
|
+
private_constant :COMMAND_CONFIG, :COMMAND_HELP
|
26
26
|
|
27
27
|
class << self
|
28
28
|
# expect some list, but nothing to display
|
@@ -60,14 +60,10 @@ module Aspera
|
|
60
60
|
return {type: :object_list, data: status_table}
|
61
61
|
end
|
62
62
|
|
63
|
-
def
|
64
|
-
|
65
|
-
allowed_options = Preview::Terminal.method(:build).parameters.select{|i|i[0].eql?(:key)}.map{|i|i[1]}
|
66
|
-
unknown_options = terminal_options.keys - allowed_options
|
67
|
-
raise "invalid options: #{unknown_options.join(', ')}, use #{allowed_options.join(', ')}" unless unknown_options.empty?
|
68
|
-
return Main.result_status(Preview::Terminal.build(blob, **terminal_options))
|
63
|
+
def result_image(blob, formatter:)
|
64
|
+
return Main.result_status(formatter.status_image(blob))
|
69
65
|
end
|
70
|
-
end
|
66
|
+
end
|
71
67
|
|
72
68
|
private
|
73
69
|
|
@@ -88,6 +84,8 @@ module Aspera
|
|
88
84
|
@option_help = false
|
89
85
|
@option_show_config = false
|
90
86
|
@bash_completion = false
|
87
|
+
early_debug_setup
|
88
|
+
Log.log.trace2{Log.dump(:argv, @argv)}
|
91
89
|
end
|
92
90
|
|
93
91
|
# This can throw exception if there is a problem with the environment, needs to be caught by execute method
|
@@ -95,8 +93,6 @@ module Aspera
|
|
95
93
|
@plug_init[:only_manual] = false
|
96
94
|
# create formatter, in case there is an exception, it is used to display.
|
97
95
|
@plug_init[:formatter] = Formatter.new
|
98
|
-
# second : manage debug level (allows debugging of option parser)
|
99
|
-
early_debug_setup
|
100
96
|
@plug_init[:options] = Manager.new(PROGRAM_NAME)
|
101
97
|
# give command line arguments to option manager
|
102
98
|
options.parse_command_line(@argv)
|
@@ -164,10 +160,9 @@ module Aspera
|
|
164
160
|
options.declare(:bash_comp, 'Generate bash completion for command', values: :none) { @bash_completion = true }
|
165
161
|
options.declare(:show_config, 'Display parameters used for the provided action', values: :none) { @option_show_config = true }
|
166
162
|
options.declare(:version, 'Display version', values: :none, short: 'v') { formatter.display_message(:data, Cli::VERSION); Process.exit(0) } # rubocop:disable Style/Semicolon, Layout/LineLength
|
167
|
-
options.declare(:warnings, 'Check for language warnings', values: :none, short: 'w') { $VERBOSE = true }
|
168
163
|
options.declare(
|
169
164
|
:ui, 'Method to start browser',
|
170
|
-
values: OpenApplication
|
165
|
+
values: OpenApplication::USER_INTERFACES,
|
171
166
|
handler: {o: OpenApplication.instance, m: :url_method},
|
172
167
|
default: OpenApplication.default_gui_mode)
|
173
168
|
options.declare(:log_level, 'Log level', values: Log.levels, handler: {o: Log.instance, m: :level})
|
@@ -187,17 +182,15 @@ module Aspera
|
|
187
182
|
def get_plugin_instance_with_options(plugin_name_sym, env=nil)
|
188
183
|
env ||= @plug_init
|
189
184
|
Log.log.debug{"get_plugin_instance_with_options(#{plugin_name_sym})"}
|
190
|
-
require PluginFactory.instance.plugins[plugin_name_sym][:require_stanza]
|
191
185
|
# load default params only if no param already loaded before plugin instantiation
|
192
186
|
env[:config].add_plugin_default_preset(plugin_name_sym)
|
193
187
|
command_plugin = PluginFactory.instance.create(plugin_name_sym, **env)
|
194
|
-
Log.log.debug{"got #{command_plugin.class}"}
|
195
188
|
return command_plugin
|
196
189
|
end
|
197
190
|
|
198
191
|
def generate_bash_completion
|
199
192
|
if options.get_next_argument('', expected: :multiple, mandatory: false).nil?
|
200
|
-
PluginFactory.instance.
|
193
|
+
PluginFactory.instance.plugin_list.each{|p|puts p}
|
201
194
|
else
|
202
195
|
Log.log.warn('only first level completion so far')
|
203
196
|
end
|
@@ -210,8 +203,8 @@ module Aspera
|
|
210
203
|
formatter.display_message(:error, options.parser)
|
211
204
|
if all_plugins
|
212
205
|
# list plugins that have a "require" field, i.e. all but main plugin
|
213
|
-
PluginFactory.instance.
|
214
|
-
next if plugin_name_sym.eql?(
|
206
|
+
PluginFactory.instance.plugin_list.each do |plugin_name_sym|
|
207
|
+
next if plugin_name_sym.eql?(COMMAND_CONFIG)
|
215
208
|
# override main option parser with a brand new, to avoid having global options
|
216
209
|
plugin_env = @plug_init.clone
|
217
210
|
plugin_env[:only_manual] = true # force declaration of all options
|
@@ -261,17 +254,17 @@ module Aspera
|
|
261
254
|
config.periodic_check_newer_gem_version
|
262
255
|
command_sym =
|
263
256
|
if @option_show_config && options.command_or_arg_empty?
|
264
|
-
|
257
|
+
COMMAND_CONFIG
|
265
258
|
else
|
266
|
-
options.get_next_command(PluginFactory.instance.
|
259
|
+
options.get_next_command(PluginFactory.instance.plugin_list.unshift(COMMAND_HELP))
|
267
260
|
end
|
268
261
|
# command will not be executed, but we need manual
|
269
262
|
options.fail_on_missing_mandatory = false if @option_help || @option_show_config
|
270
263
|
# main plugin is not dynamically instantiated
|
271
264
|
case command_sym
|
272
|
-
when
|
265
|
+
when COMMAND_HELP
|
273
266
|
exit_with_usage(true)
|
274
|
-
when
|
267
|
+
when COMMAND_CONFIG
|
275
268
|
command_plugin = config
|
276
269
|
else
|
277
270
|
# get plugin, set options, etc
|
@@ -282,7 +275,7 @@ module Aspera
|
|
282
275
|
# help requested for current plugin
|
283
276
|
exit_with_usage(false) if @option_help
|
284
277
|
if @option_show_config
|
285
|
-
formatter.display_results(
|
278
|
+
formatter.display_results(type: :single_object, data: options.known_options(only_defined: true).stringify_keys)
|
286
279
|
execute_command = false
|
287
280
|
end
|
288
281
|
# locking for single execution (only after "per plugin" option, in case lock port is there)
|
@@ -304,7 +297,7 @@ module Aspera
|
|
304
297
|
at_exit{File.delete(pid_file)}
|
305
298
|
end
|
306
299
|
# execute and display (if not exclusive execution)
|
307
|
-
formatter.display_results(command_plugin.execute_action) if execute_command
|
300
|
+
formatter.display_results(**command_plugin.execute_action) if execute_command
|
308
301
|
# save config file if command modified it
|
309
302
|
config.save_config_file_if_needed
|
310
303
|
# finish
|
@@ -347,7 +340,7 @@ module Aspera
|
|
347
340
|
Process.exit(1)
|
348
341
|
end
|
349
342
|
return nil
|
350
|
-
end
|
351
|
-
end
|
352
|
-
end
|
353
|
-
end
|
343
|
+
end
|
344
|
+
end
|
345
|
+
end
|
346
|
+
end
|