aspera-cli 4.17.0 → 4.18.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|