aspera-cli 4.22.0 → 4.24.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 +405 -364
- data/CONTRIBUTING.md +86 -29
- data/README.md +1856 -961
- data/bin/ascli +2 -1
- data/bin/asession +4 -4
- data/lib/aspera/agent/base.rb +4 -0
- data/lib/aspera/agent/connect.rb +20 -18
- data/lib/aspera/agent/desktop.rb +14 -11
- data/lib/aspera/agent/direct.rb +39 -31
- data/lib/aspera/agent/httpgw.rb +2 -2
- data/lib/aspera/agent/node.rb +9 -11
- data/lib/aspera/agent/transferd.rb +18 -11
- data/lib/aspera/api/aoc.rb +53 -43
- data/lib/aspera/api/cos_node.rb +7 -5
- data/lib/aspera/api/httpgw.rb +23 -22
- data/lib/aspera/api/node.rb +104 -22
- data/lib/aspera/ascmd.rb +35 -21
- data/lib/aspera/ascp/installation.rb +43 -43
- data/lib/aspera/ascp/management.rb +5 -4
- data/lib/aspera/assert.rb +55 -24
- data/lib/aspera/cli/basic_auth_plugin.rb +8 -7
- data/lib/aspera/cli/error.rb +1 -1
- data/lib/aspera/cli/extended_value.rb +28 -29
- data/lib/aspera/cli/formatter.rb +191 -168
- data/lib/aspera/cli/hints.rb +38 -4
- data/lib/aspera/cli/main.rb +139 -108
- data/lib/aspera/cli/manager.rb +51 -31
- data/lib/aspera/cli/plugin.rb +149 -78
- data/lib/aspera/cli/plugin_factory.rb +2 -2
- data/lib/aspera/cli/plugins/aoc.rb +217 -88
- data/lib/aspera/cli/plugins/ats.rb +15 -13
- data/lib/aspera/cli/plugins/config.rb +105 -227
- data/lib/aspera/cli/plugins/console.rb +49 -18
- data/lib/aspera/cli/plugins/cos.rb +4 -4
- data/lib/aspera/cli/plugins/faspex.rb +45 -51
- data/lib/aspera/cli/plugins/faspex5.rb +162 -163
- data/lib/aspera/cli/plugins/faspio.rb +6 -5
- data/lib/aspera/cli/plugins/httpgw.rb +2 -2
- data/lib/aspera/cli/plugins/node.rb +233 -247
- data/lib/aspera/cli/plugins/orchestrator.rb +10 -14
- data/lib/aspera/cli/plugins/preview.rb +26 -29
- data/lib/aspera/cli/plugins/server.rb +29 -28
- data/lib/aspera/cli/plugins/shares.rb +40 -28
- data/lib/aspera/cli/sync_actions.rb +101 -80
- data/lib/aspera/cli/transfer_agent.rb +55 -58
- data/lib/aspera/cli/transfer_progress.rb +29 -20
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/cli/wizard.rb +160 -0
- data/lib/aspera/colors.rb +13 -8
- data/lib/aspera/command_line_builder.rb +28 -22
- data/lib/aspera/command_line_converter.rb +31 -0
- data/lib/aspera/data_repository.rb +1 -0
- data/lib/aspera/environment.rb +144 -100
- data/lib/aspera/faspex_gw.rb +1 -1
- data/lib/aspera/faspex_postproc.rb +3 -2
- data/lib/aspera/hash_ext.rb +1 -1
- data/lib/aspera/id_generator.rb +10 -10
- data/lib/aspera/keychain/base.rb +18 -0
- data/lib/aspera/keychain/encrypted_hash.rb +6 -12
- data/lib/aspera/keychain/factory.rb +9 -3
- data/lib/aspera/keychain/hashicorp_vault.rb +9 -6
- data/lib/aspera/keychain/macos_security.rb +13 -13
- data/lib/aspera/log.rb +70 -20
- data/lib/aspera/nagios.rb +5 -6
- data/lib/aspera/node_simulator.rb +12 -7
- data/lib/aspera/oauth/base.rb +6 -2
- data/lib/aspera/oauth/factory.rb +25 -18
- data/lib/aspera/oauth/jwt.rb +13 -1
- data/lib/aspera/oauth/url_json.rb +3 -3
- data/lib/aspera/oauth/web.rb +5 -3
- data/lib/aspera/persistency_folder.rb +2 -2
- data/lib/aspera/preview/file_types.rb +43 -35
- data/lib/aspera/preview/generator.rb +26 -13
- data/lib/aspera/preview/terminal.rb +10 -7
- data/lib/aspera/preview/utils.rb +11 -9
- data/lib/aspera/products/connect.rb +2 -1
- data/lib/aspera/products/desktop.rb +1 -1
- data/lib/aspera/products/other.rb +2 -2
- data/lib/aspera/products/transferd.rb +8 -6
- data/lib/aspera/proxy_auto_config.rb +1 -1
- data/lib/aspera/rest.rb +46 -28
- data/lib/aspera/rest_call_error.rb +1 -1
- data/lib/aspera/rest_error_analyzer.rb +1 -0
- data/lib/aspera/resumer.rb +1 -1
- data/lib/aspera/secret_hider.rb +46 -40
- data/lib/aspera/ssh.rb +14 -4
- data/lib/aspera/sync/args.schema.yaml +102 -0
- data/lib/aspera/sync/conf.schema.yaml +701 -0
- data/lib/aspera/sync/database.rb +83 -0
- data/lib/aspera/{transfer/sync.rb → sync/operations.rb} +145 -68
- data/lib/aspera/temp_file_manager.rb +4 -2
- data/lib/aspera/timer_limiter.rb +7 -5
- data/lib/aspera/transfer/error.rb +1 -1
- data/lib/aspera/transfer/error_info.rb +1 -2
- data/lib/aspera/transfer/faux_file.rb +11 -10
- data/lib/aspera/transfer/parameters.rb +6 -5
- data/lib/aspera/transfer/spec.rb +15 -1
- data/lib/aspera/transfer/spec.schema.yaml +316 -293
- data/lib/aspera/transfer/spec_doc.rb +34 -16
- data/lib/aspera/transfer/uri.rb +5 -5
- data/lib/aspera/uri_reader.rb +14 -10
- data/lib/aspera/web_auth.rb +2 -2
- data/lib/aspera/web_server_simple.rb +2 -2
- data.tar.gz.sig +0 -0
- metadata +15 -15
- metadata.gz.sig +0 -0
- data/examples/dascli +0 -30
- data/examples/get_proto_file.rb +0 -8
- data/examples/proxy.pac +0 -60
- data/lib/aspera/transfer/convert.rb +0 -29
- data/lib/aspera/transfer/sync_instance.schema.yaml +0 -13
- data/lib/aspera/transfer/sync_session.schema.yaml +0 -79
data/lib/aspera/cli/formatter.rb
CHANGED
@@ -11,81 +11,15 @@ require 'terminal-table'
|
|
11
11
|
require 'tty-spinner'
|
12
12
|
require 'yaml'
|
13
13
|
require 'pp'
|
14
|
+
require 'csv'
|
14
15
|
require 'word_wrap'
|
15
16
|
|
16
17
|
module Aspera
|
17
18
|
module Cli
|
18
|
-
#
|
19
|
-
class Flattener
|
20
|
-
def initialize(formatter)
|
21
|
-
@result = nil
|
22
|
-
@formatter = formatter
|
23
|
-
end
|
24
|
-
|
25
|
-
# General method
|
26
|
-
def flatten(something)
|
27
|
-
Aspera.assert_type(something, Hash)
|
28
|
-
@result = {}
|
29
|
-
flatten_any(something, '')
|
30
|
-
return @result
|
31
|
-
end
|
32
|
-
|
33
|
-
private
|
34
|
-
|
35
|
-
# Recursive function to flatten any type
|
36
|
-
# @param something [Object] to be flattened
|
37
|
-
# @param name [String] name of englobing key
|
38
|
-
def flatten_any(something, name)
|
39
|
-
if something.is_a?(Hash)
|
40
|
-
flattened_hash(something, name)
|
41
|
-
elsif something.is_a?(Array)
|
42
|
-
flatten_array(something, name)
|
43
|
-
elsif something.is_a?(String) && something.empty?
|
44
|
-
@result[name] = @formatter.special_format('empty string')
|
45
|
-
elsif something.nil?
|
46
|
-
@result[name] = @formatter.special_format('null')
|
47
|
-
# elsif something.eql?(true) || something.eql?(false)
|
48
|
-
# @result[name] = something
|
49
|
-
else
|
50
|
-
@result[name] = something
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
# Recursive function to flatten an array
|
55
|
-
# @param array [Array] to be flattened
|
56
|
-
# @param name [String] name of englobing key
|
57
|
-
def flatten_array(array, name)
|
58
|
-
if array.empty?
|
59
|
-
@result[name] = @formatter.special_format('empty list')
|
60
|
-
elsif array.all?(String)
|
61
|
-
@result[name] = array.join("\n")
|
62
|
-
elsif array.all?{ |i| i.is_a?(Hash) && i.keys.eql?(%w[name])}
|
63
|
-
@result[name] = array.map(&:values).join(', ')
|
64
|
-
elsif array.all?{ |i| i.is_a?(Hash) && i.keys.sort.eql?(%w[name value])}
|
65
|
-
flattened_hash(array.each_with_object({}){ |i, h| h[i['name']] = i['value']}, name)
|
66
|
-
else
|
67
|
-
array.each_with_index{ |item, index| flatten_any(item, "#{name}.#{index}")}
|
68
|
-
end
|
69
|
-
nil
|
70
|
-
end
|
71
|
-
|
72
|
-
# Recursive function to flatten a Hash
|
73
|
-
# @param hash [Hash] to be flattened
|
74
|
-
# @param name [String] name of englobing key
|
75
|
-
def flattened_hash(hash, name)
|
76
|
-
prefix = name.empty? ? '' : "#{name}."
|
77
|
-
hash.each do |k, v|
|
78
|
-
flatten_any(v, "#{prefix}#{k}")
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
# Take care of output
|
19
|
+
# Take care of CLI output on terminal
|
84
20
|
class Formatter
|
85
21
|
# remove a fields from the list
|
86
22
|
FIELDS_LESS = '-'
|
87
|
-
CSV_RECORD_SEPARATOR = "\n"
|
88
|
-
CSV_FIELD_SEPARATOR = ','
|
89
23
|
# supported output formats
|
90
24
|
DISPLAY_FORMATS = %i[text nagios ruby json jsonpp yaml table csv image].freeze
|
91
25
|
# user output levels
|
@@ -93,34 +27,113 @@ module Aspera
|
|
93
27
|
# column names for single object display in table
|
94
28
|
SINGLE_OBJECT_COLUMN_NAMES = %i[field value].freeze
|
95
29
|
|
96
|
-
private_constant :FIELDS_LESS, :
|
30
|
+
private_constant :FIELDS_LESS, :DISPLAY_FORMATS, :DISPLAY_LEVELS, :SINGLE_OBJECT_COLUMN_NAMES
|
97
31
|
# prefix to display error messages in user messages (terminal)
|
98
32
|
ERROR_FLASH = 'ERROR:'.bg_red.gray.blink.freeze
|
99
33
|
WARNING_FLASH = 'WARNING:'.bg_brown.black.blink.freeze
|
100
34
|
HINT_FLASH = 'HINT:'.bg_green.gray.blink.freeze
|
101
35
|
|
102
36
|
class << self
|
103
|
-
|
104
|
-
|
105
|
-
return list.map{ |i| "#{FIELDS_LESS}#{i}"}.unshift(SpecialValues::ALL)
|
106
|
-
end
|
107
|
-
|
37
|
+
# nicer display for boolean
|
38
|
+
# used by spec_doc
|
108
39
|
def tick(yes)
|
109
40
|
result =
|
110
|
-
if Environment.
|
111
|
-
|
112
|
-
"\u2713"
|
113
|
-
else
|
114
|
-
"\u2717"
|
115
|
-
end
|
116
|
-
elsif yes
|
117
|
-
'Y'
|
41
|
+
if Environment.terminal_supports_unicode?
|
42
|
+
yes ? "\u2713" : "\u2717"
|
118
43
|
else
|
119
|
-
' '
|
44
|
+
yes ? 'Y' : ' '
|
120
45
|
end
|
121
46
|
return result.green if yes
|
122
47
|
return result.red
|
123
48
|
end
|
49
|
+
|
50
|
+
# Highlight special values on terminal
|
51
|
+
# empty values are dim
|
52
|
+
# used by spec_doc
|
53
|
+
def special_format(what)
|
54
|
+
result = "<#{what}>"
|
55
|
+
return %w[null empty].any?{ |s| what.include?(s)} ? result.dim : result.reverse_color
|
56
|
+
end
|
57
|
+
|
58
|
+
# for transfer spec table, build line for display in terminal
|
59
|
+
# used by spec_doc
|
60
|
+
def check_row(row)
|
61
|
+
row.each_key do |k|
|
62
|
+
row[k] = row[k].map{ |i| WordWrap.ww(i.to_s, 120).chomp}.join("\n") if row[k].is_a?(Array)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# used by spec_doc
|
67
|
+
def keyword_highlight(value)
|
68
|
+
value.bold
|
69
|
+
end
|
70
|
+
|
71
|
+
# replace empty values with a readable version
|
72
|
+
def enhance_display_values_hash(input_hash)
|
73
|
+
stack = [input_hash]
|
74
|
+
until stack.empty?
|
75
|
+
current = stack.pop
|
76
|
+
current.each do |key, value|
|
77
|
+
case value
|
78
|
+
when NilClass
|
79
|
+
current[key] = special_format('null')
|
80
|
+
when String
|
81
|
+
current[key] = special_format('empty string') if value.empty?
|
82
|
+
when Array
|
83
|
+
if value.empty?
|
84
|
+
current[key] = special_format('empty list')
|
85
|
+
else
|
86
|
+
value.each do |item|
|
87
|
+
stack.push(item) if item.is_a?(Hash)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
when Hash
|
91
|
+
if value.empty?
|
92
|
+
current[key] = special_format('empty dict')
|
93
|
+
else
|
94
|
+
stack.push(value)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Flatten a Hash into single level hash
|
102
|
+
def flatten_hash(input)
|
103
|
+
Aspera.assert_type(input, Hash)
|
104
|
+
return input if input.empty?
|
105
|
+
flat = {}
|
106
|
+
stack = [[nil, input]]
|
107
|
+
until stack.empty?
|
108
|
+
prefix, current = stack.pop
|
109
|
+
if current.respond_to?(:empty?) && current.empty?
|
110
|
+
flat[prefix] = current
|
111
|
+
next
|
112
|
+
end
|
113
|
+
case current
|
114
|
+
when Hash
|
115
|
+
current.reverse_each{ |k, v| stack.push([[prefix, k].compact.join('.'), v])}
|
116
|
+
when Array
|
117
|
+
if current.all?(String)
|
118
|
+
flat[prefix] = current.join("\n")
|
119
|
+
elsif current.all?{ |i| i.is_a?(Hash) && i.keys == ['name']}
|
120
|
+
flat[prefix] = current.map{ |i| i['name']}.join(', ')
|
121
|
+
elsif current.all?{ |i| i.is_a?(Hash) && i.keys.sort == %w[name value]}
|
122
|
+
stack.push([prefix, current.each_with_object({}){ |i, h| h[i['name']] = i['value']}])
|
123
|
+
else
|
124
|
+
current.each_with_index.reverse_each{ |v, k| stack.push([[prefix, k].compact.join('.'), v])}
|
125
|
+
end
|
126
|
+
else
|
127
|
+
flat[prefix] = current
|
128
|
+
end
|
129
|
+
end
|
130
|
+
flat
|
131
|
+
end
|
132
|
+
|
133
|
+
def all_but(list)
|
134
|
+
list = [list] unless list.is_a?(Array)
|
135
|
+
return list.map{ |i| "#{FIELDS_LESS}#{i}"}.unshift(SpecialValues::ALL)
|
136
|
+
end
|
124
137
|
end
|
125
138
|
|
126
139
|
# initialize the formatter
|
@@ -129,19 +142,6 @@ module Aspera
|
|
129
142
|
@spinner = nil
|
130
143
|
end
|
131
144
|
|
132
|
-
# Highlight special values on terminal
|
133
|
-
def special_format(what)
|
134
|
-
result = "<#{what}>"
|
135
|
-
return %w[null empty].any?{ |s| what.include?(s)} ? result.dim : result.reverse_color
|
136
|
-
end
|
137
|
-
|
138
|
-
# for transfer spec table, build line for display
|
139
|
-
def check_row(row)
|
140
|
-
row.each_key do |k|
|
141
|
-
row[k] = row[k].map{ |i| WordWrap.ww(i.to_s, 120).chomp}.join("\n") if row[k].is_a?(Array)
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
145
|
# call this after REST calls if several api calls are expected
|
146
146
|
def long_operation_running(title = '')
|
147
147
|
return unless Environment.terminal?
|
@@ -159,7 +159,7 @@ module Aspera
|
|
159
159
|
end
|
160
160
|
|
161
161
|
def declare_options(options)
|
162
|
-
default_table_style = if Environment.
|
162
|
+
default_table_style = if Environment.terminal_supports_unicode?
|
163
163
|
{border: :unicode_round}
|
164
164
|
else
|
165
165
|
{}
|
@@ -170,20 +170,22 @@ module Aspera
|
|
170
170
|
options.declare(
|
171
171
|
:fields, "Comma separated list of: fields, or #{SpecialValues::ALL}, or #{SpecialValues::DEF}", handler: {o: self, m: :option_handler},
|
172
172
|
types: [String, Array, Regexp, Proc],
|
173
|
-
default: SpecialValues::DEF
|
173
|
+
default: SpecialValues::DEF
|
174
|
+
)
|
174
175
|
options.declare(:select, 'Select only some items in lists: column, value', types: [Hash, Proc], handler: {o: self, m: :option_handler})
|
175
|
-
options.declare(:table_style, 'Table
|
176
|
+
options.declare(:table_style, '(Table) Display style', types: [Hash], handler: {o: self, m: :option_handler}, default: default_table_style)
|
176
177
|
options.declare(:flat_hash, '(Table) Display deep values as additional keys', values: :bool, handler: {o: self, m: :option_handler}, default: true)
|
177
178
|
options.declare(
|
178
179
|
:multi_single, '(Table) Control how object list is displayed as single table, or multiple objects', values: %i[no yes single],
|
179
|
-
handler: {o: self, m: :option_handler}, default: :no
|
180
|
+
handler: {o: self, m: :option_handler}, default: :no
|
181
|
+
)
|
180
182
|
options.declare(:show_secrets, 'Show secrets on command output', values: :bool, handler: {o: self, m: :option_handler}, default: false)
|
181
183
|
options.declare(:image, 'Options for image display', types: Hash, handler: {o: self, m: :option_handler}, default: {})
|
182
184
|
end
|
183
185
|
|
184
186
|
# method accessed by option manager
|
185
187
|
# options are: format, output, display, fields, select, table_style, flat_hash, multi_single
|
186
|
-
def option_handler(option_symbol, operation, value=nil)
|
188
|
+
def option_handler(option_symbol, operation, value = nil)
|
187
189
|
Aspera.assert_values(operation, %i[set get])
|
188
190
|
case operation
|
189
191
|
when :set
|
@@ -214,11 +216,11 @@ module Aspera
|
|
214
216
|
# info: additional info, displayed if level==info
|
215
217
|
# error: always displayed on stderr
|
216
218
|
def display_message(message_level, message, hide_secrets: true)
|
217
|
-
message = SecretHider.hide_secrets_in_string(message) if hide_secrets && message.is_a?(String) && hide_secrets?
|
219
|
+
message = SecretHider.instance.hide_secrets_in_string(message) if hide_secrets && message.is_a?(String) && hide_secrets?
|
218
220
|
case message_level
|
219
221
|
when :data then $stdout.puts(message) unless @options[:display].eql?(:error)
|
220
222
|
when :info then $stdout.puts(message) if @options[:display].eql?(:info)
|
221
|
-
when :error then $stderr.puts(message)
|
223
|
+
when :error then $stderr.puts(message) # rubocop:disable Style/StderrPuts
|
222
224
|
else Aspera.error_unexpected_value(message_level)
|
223
225
|
end
|
224
226
|
end
|
@@ -242,7 +244,7 @@ module Aspera
|
|
242
244
|
|
243
245
|
# hides secrets in Hash or Array
|
244
246
|
def hide_secrets(data)
|
245
|
-
SecretHider.deep_remove_secret(data) if hide_secrets?
|
247
|
+
SecretHider.instance.deep_remove_secret(data) if hide_secrets?
|
246
248
|
end
|
247
249
|
|
248
250
|
# this method displays the results, especially the table format
|
@@ -252,13 +254,14 @@ module Aspera
|
|
252
254
|
# @param fields [Array<String>] list of fields to display
|
253
255
|
# @param name [String] name of the column to display
|
254
256
|
def display_results(type:, data: nil, total: nil, fields: nil, name: nil)
|
255
|
-
Log.log.debug{"display_results:
|
256
|
-
Log.log.trace1{"display_results:data=#{data}"}
|
257
|
+
Log.log.debug{"display_results: type=#{type} class=#{data.class}"}
|
258
|
+
Log.log.trace1{"display_results: data=#{data}"}
|
257
259
|
Aspera.assert_type(type, Symbol){'result must have type'}
|
258
260
|
Aspera.assert(!data.nil? || %i[empty nothing].include?(type)){'result must have data'}
|
259
261
|
display_item_count(data.length, total) unless total.nil?
|
260
262
|
hide_secrets(data)
|
261
|
-
data = SecretHider.hide_secrets_in_string(data) if data.is_a?(String) && hide_secrets?
|
263
|
+
data = SecretHider.instance.hide_secrets_in_string(data) if data.is_a?(String) && hide_secrets?
|
264
|
+
@options[:format] = :image if type.eql?(:image)
|
262
265
|
case @options[:format]
|
263
266
|
when :text
|
264
267
|
display_message(:data, data.to_s)
|
@@ -273,87 +276,90 @@ module Aspera
|
|
273
276
|
when :yaml
|
274
277
|
display_message(:data, YAML.dump(filter_list_on_fields(data)))
|
275
278
|
when :image
|
276
|
-
#
|
277
|
-
url = data
|
279
|
+
# if object or list, then must be a single
|
278
280
|
case type
|
279
281
|
when :single_object, :object_list
|
280
|
-
|
281
|
-
raise 'image display requires a single result' unless
|
282
|
-
fields = compute_fields(
|
283
|
-
raise 'select a field to display' unless fields.length == 1
|
284
|
-
|
285
|
-
raise 'no such field' unless
|
286
|
-
|
282
|
+
data = [data] if type.eql?(:single_object)
|
283
|
+
raise BadArgument, 'image display requires a single result' unless data.length == 1
|
284
|
+
fields = compute_fields(data, fields)
|
285
|
+
raise BadArgument, 'select a single field to display' unless fields.length == 1
|
286
|
+
data = data.first
|
287
|
+
raise BadArgument, 'no such field' unless data.key?(fields.first)
|
288
|
+
data = data[fields.first]
|
289
|
+
end
|
290
|
+
Aspera.assert_type(data, String){'URL or blob for image'}
|
291
|
+
# Check if URL
|
292
|
+
data =
|
293
|
+
begin
|
294
|
+
# just validate
|
295
|
+
URI.parse(data)
|
296
|
+
if Environment.instance.url_method.eql?(:text)
|
297
|
+
UriReader.read(data)
|
298
|
+
else
|
299
|
+
Environment.instance.open_uri(data)
|
300
|
+
display_message(:info, "Opened URL in browser: #{data}")
|
301
|
+
:done
|
302
|
+
end
|
303
|
+
rescue URI::InvalidURIError
|
304
|
+
data
|
305
|
+
end
|
306
|
+
# try base64
|
307
|
+
data = begin
|
308
|
+
Base64.strict_decode64(data)
|
309
|
+
rescue
|
310
|
+
data
|
287
311
|
end
|
288
|
-
|
289
|
-
display_message(:data,
|
312
|
+
# here, data is the image blob
|
313
|
+
display_message(:data, Preview::Terminal.build(data, **@options[:image].symbolize_keys)) unless data.eql?(:done)
|
290
314
|
when :table, :csv
|
291
315
|
case type
|
292
|
-
when :
|
293
|
-
|
294
|
-
|
295
|
-
|
316
|
+
when :single_object
|
317
|
+
# :single_object is a Hash, where key=colum name
|
318
|
+
Aspera.assert_type(data, Hash)
|
319
|
+
if data.empty?
|
320
|
+
display_message(:data, self.class.special_format('empty dict'))
|
321
|
+
else
|
322
|
+
data = self.class.flatten_hash(data) if @options[:flat_hash]
|
323
|
+
display_table([data], compute_fields([data], fields), single: true)
|
296
324
|
end
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
325
|
+
when :object_list
|
326
|
+
# :object_list is an Array of Hash, where key=colum name
|
327
|
+
Aspera.assert_type(data, Array)
|
328
|
+
Aspera.assert(data.all?(Hash)){"expecting Array of Hash: #{data.inspect}"}
|
329
|
+
data = data.map{ |obj| self.class.flatten_hash(obj)} if @options[:flat_hash]
|
330
|
+
display_table(data, compute_fields(data, fields), single: type.eql?(:single_object))
|
302
331
|
when :value_list
|
303
|
-
# :value_list is a simple array of values, name of column provided in
|
332
|
+
# :value_list is a simple array of values, name of column provided in `name`
|
304
333
|
display_table(data.map{ |i| {name => i}}, [name])
|
305
|
-
when :
|
306
|
-
|
334
|
+
when :special # no table
|
335
|
+
if data.eql?(:nothing)
|
336
|
+
Log.log.debug('no result expected')
|
337
|
+
return
|
338
|
+
end
|
339
|
+
display_message(:info, self.class.special_format(data.to_s))
|
307
340
|
return
|
308
|
-
when :nothing # no result expected
|
309
|
-
Log.log.debug('no result expected')
|
310
341
|
when :status # no table
|
311
342
|
# :status displays a simple message
|
312
343
|
display_message(:info, data)
|
313
344
|
when :text # no table
|
314
345
|
# :status displays a simple message
|
315
346
|
display_message(:data, data)
|
316
|
-
else
|
317
|
-
raise "unknown data type: #{type}"
|
347
|
+
else Aspera.error_unexpected_value(type){'data type'}
|
318
348
|
end
|
319
|
-
else
|
320
|
-
raise "not expected: #{@options[:format]}"
|
349
|
+
else Aspera.error_unexpected_value(@options[:format]){'format'}
|
321
350
|
end
|
322
351
|
end
|
323
|
-
|
324
|
-
# @return text suitable to display an image from url
|
325
|
-
# @param blob [String] either a URL or image data
|
326
|
-
def status_image(blob)
|
327
|
-
# check if URL
|
328
|
-
begin
|
329
|
-
URI.parse(blob)
|
330
|
-
url = blob
|
331
|
-
unless Environment.instance.url_method.eql?(:text)
|
332
|
-
Environment.instance.open_uri(url)
|
333
|
-
return ''
|
334
|
-
end
|
335
|
-
blob = UriReader.read(url)
|
336
|
-
rescue URI::InvalidURIError
|
337
|
-
nil
|
338
|
-
end
|
339
|
-
# try base64
|
340
|
-
begin
|
341
|
-
blob = Base64.strict_decode64(blob)
|
342
|
-
rescue
|
343
|
-
nil
|
344
|
-
end
|
345
|
-
return Preview::Terminal.build(blob, **@options[:image].symbolize_keys)
|
346
|
-
end
|
347
352
|
#==========================================================================================
|
348
353
|
|
349
354
|
private
|
350
355
|
|
356
|
+
# @return all fields of all objects in list of objects
|
351
357
|
def all_fields(data)
|
352
358
|
data.each_with_object({}){ |v, m| v.each_key{ |c| m[c] = true}}.keys
|
353
359
|
end
|
354
360
|
|
355
361
|
# @return the list of fields to display
|
356
|
-
# @param data
|
362
|
+
# @param data [Array<Hash>] data to display
|
357
363
|
# @param default [Array<String>, Proc] list of fields to display by default (may contain special values)
|
358
364
|
def compute_fields(data, default)
|
359
365
|
Log.log.debug{"compute_fields: data:#{data.class} default:#{default.class} #{default}"}
|
@@ -412,7 +418,11 @@ module Aspera
|
|
412
418
|
def filter_columns_on_select(data)
|
413
419
|
case @options[:select]
|
414
420
|
when Proc
|
415
|
-
|
421
|
+
begin
|
422
|
+
data.select!{ |i| @options[:select].call(i)}
|
423
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
424
|
+
raise Cli::BadArgument, "Error in user-provided ruby lambda code during select: #{e.message}"
|
425
|
+
end
|
416
426
|
when Hash
|
417
427
|
@options[:select].each{ |k, v| data.select!{ |i| i[k].eql?(v)}}
|
418
428
|
end
|
@@ -423,12 +433,13 @@ module Aspera
|
|
423
433
|
# @param fields [Array] list of column names
|
424
434
|
def display_table(object_array, fields, single: false)
|
425
435
|
Aspera.assert(!fields.nil?){'missing fields parameter'}
|
426
|
-
filter_columns_on_select(object_array)
|
427
436
|
if object_array.empty?
|
428
437
|
# no display for csv
|
429
|
-
display_message(:info, special_format('empty')) if @options[:format].eql?(:table)
|
438
|
+
display_message(:info, self.class.special_format('empty')) if @options[:format].eql?(:table)
|
430
439
|
return
|
431
440
|
end
|
441
|
+
filter_columns_on_select(object_array)
|
442
|
+
object_array.each{ |i| self.class.enhance_display_values_hash(i)}
|
432
443
|
# if table has only one element, and only one field, display the value
|
433
444
|
if object_array.length == 1 && fields.length == 1
|
434
445
|
Log.log.debug("display_table: single element, field: #{fields.first}")
|
@@ -441,7 +452,7 @@ module Aspera
|
|
441
452
|
fields = all_fields(object_array)
|
442
453
|
single = false
|
443
454
|
end
|
444
|
-
Log.
|
455
|
+
Log.dump(:object_array, object_array)
|
445
456
|
# convert data to string, and keep only display fields
|
446
457
|
final_table_rows = object_array.map{ |r| fields.map{ |c| r[c].to_s}}
|
447
458
|
# remove empty rows
|
@@ -456,17 +467,29 @@ module Aspera
|
|
456
467
|
display_message(:data, Terminal::Table.new(
|
457
468
|
headings: SINGLE_OBJECT_COLUMN_NAMES,
|
458
469
|
rows: fields.zip(row),
|
459
|
-
style: @options[:table_style]
|
470
|
+
style: @options[:table_style].symbolize_keys
|
471
|
+
))
|
460
472
|
end
|
461
473
|
else
|
462
474
|
# display the table ! as single table
|
463
475
|
display_message(:data, Terminal::Table.new(
|
464
476
|
headings: fields,
|
465
477
|
rows: final_table_rows,
|
466
|
-
style: @options[:table_style]
|
478
|
+
style: @options[:table_style].symbolize_keys
|
479
|
+
))
|
467
480
|
end
|
468
481
|
when :csv
|
469
|
-
|
482
|
+
params = @options[:table_style].symbolize_keys
|
483
|
+
# delete default
|
484
|
+
params.delete(:border)
|
485
|
+
add_headers = params.delete(:headers)
|
486
|
+
output = CSV.generate(**params) do |csv|
|
487
|
+
csv << fields if add_headers
|
488
|
+
final_table_rows.each do |row|
|
489
|
+
csv << row
|
490
|
+
end
|
491
|
+
end
|
492
|
+
display_message(:data, output)
|
470
493
|
else
|
471
494
|
raise "not expected: #{@options[:format]}"
|
472
495
|
end
|
data/lib/aspera/cli/hints.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
require 'aspera/transfer/error'
|
4
4
|
require 'aspera/rest'
|
5
5
|
require 'aspera/log'
|
6
|
+
require 'aspera/ssh'
|
6
7
|
require 'aspera/assert'
|
7
8
|
require 'aspera/cli/info'
|
8
9
|
require 'net/ssh'
|
@@ -36,8 +37,9 @@ module Aspera
|
|
36
37
|
exception: OpenSSL::SSL::SSLError,
|
37
38
|
match: /(does not match the server certificate|certificate verify failed)/,
|
38
39
|
remediation: [
|
39
|
-
'You can ignore SSL errors with
|
40
|
-
'--insecure=yes'
|
40
|
+
'You can ignore SSL errors with either of the following options:',
|
41
|
+
'--insecure=yes (global skip)',
|
42
|
+
'--ignore-certificate=@list,https://<address>[:<port>] (selective skip)'
|
41
43
|
]
|
42
44
|
},
|
43
45
|
{
|
@@ -64,14 +66,46 @@ module Aspera
|
|
64
66
|
remediation: [
|
65
67
|
'If remote node is Cloud Pak For Integration',
|
66
68
|
'Make sure that a LoadBalancer is active on cluster',
|
67
|
-
'Check the external address of Aspera tcp-proxy
|
69
|
+
'Check the external address of Aspera tcp-proxy Pod'
|
70
|
+
]
|
71
|
+
},
|
72
|
+
{
|
73
|
+
exception: Aspera::RestCallError,
|
74
|
+
match: /Invalid subject\./,
|
75
|
+
remediation: [
|
76
|
+
'It seems that this user name is not registered on the server',
|
77
|
+
'Check the user name and try again'
|
78
|
+
]
|
79
|
+
},
|
80
|
+
{
|
81
|
+
exception: OpenSSL::Cipher::CipherError,
|
82
|
+
match: /authentication tag already generated by cipher/,
|
83
|
+
remediation: [
|
84
|
+
'If using JRuby, refer to aspera-cli documentation.',
|
85
|
+
'Look for "unsupported algorithm".'
|
86
|
+
]
|
87
|
+
},
|
88
|
+
{
|
89
|
+
exception: OpenSSL::SSL::SSLError,
|
90
|
+
match: /unexpected eof while reading/,
|
91
|
+
remediation: [
|
92
|
+
'Look at parameter ssl_options in http_options.',
|
93
|
+
'Refer to the manual for more information.',
|
94
|
+
%q{Try: --http-options=@json:'{"ssl_options":["IGNORE_UNEXPECTED_EOF"]}'}
|
95
|
+
]
|
96
|
+
},
|
97
|
+
{
|
98
|
+
exception: Aspera::Ssh::Error,
|
99
|
+
match: /Could not chdir to home directory/,
|
100
|
+
remediation: [
|
101
|
+
'home not created in Windows?'
|
68
102
|
]
|
69
103
|
}
|
70
104
|
]
|
71
|
-
|
72
105
|
private_constant :ERROR_HINTS
|
73
106
|
|
74
107
|
class << self
|
108
|
+
# @param error [Exception] exception object
|
75
109
|
def hint_for(error, formatter)
|
76
110
|
ERROR_HINTS.each do |hint|
|
77
111
|
next unless error.is_a?(hint[:exception])
|