aspera-cli 4.15.0 → 4.17.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/BUGS.md +29 -3
- data/CHANGELOG.md +375 -280
- data/CONTRIBUTING.md +71 -18
- data/README.md +1978 -1656
- data/bin/ascli +13 -31
- data/bin/asession +32 -22
- data/examples/dascli +2 -2
- data/lib/aspera/agent/alpha.rb +117 -0
- data/lib/aspera/agent/base.rb +61 -0
- data/lib/aspera/{fasp/agent_connect.rb → agent/connect.rb} +13 -11
- data/lib/aspera/{fasp/agent_direct.rb → agent/direct.rb} +116 -116
- data/lib/aspera/{fasp/agent_httpgw.rb → agent/httpgw.rb} +21 -19
- data/lib/aspera/{fasp/agent_node.rb → agent/node.rb} +21 -33
- data/lib/aspera/agent/trsdk.rb +188 -0
- data/lib/aspera/api/aoc.rb +586 -0
- data/lib/aspera/api/ats.rb +46 -0
- data/lib/aspera/api/cos_node.rb +95 -0
- data/lib/aspera/api/node.rb +344 -0
- data/lib/aspera/ascmd.rb +47 -14
- data/lib/aspera/{fasp → ascp}/installation.rb +54 -15
- data/lib/aspera/{fasp → ascp}/management.rb +14 -14
- data/lib/aspera/{fasp → ascp}/products.rb +1 -1
- data/lib/aspera/assert.rb +45 -0
- data/lib/aspera/cli/basic_auth_plugin.rb +11 -10
- data/lib/aspera/cli/extended_value.rb +5 -5
- data/lib/aspera/cli/formatter.rb +27 -14
- data/lib/aspera/cli/hints.rb +7 -6
- data/lib/aspera/cli/main.rb +49 -29
- data/lib/aspera/cli/manager.rb +46 -36
- data/lib/aspera/cli/plugin.rb +34 -20
- data/lib/aspera/cli/plugin_factory.rb +61 -0
- data/lib/aspera/cli/plugins/alee.rb +7 -7
- data/lib/aspera/cli/plugins/aoc.rb +168 -132
- data/lib/aspera/cli/plugins/ats.rb +33 -33
- data/lib/aspera/cli/plugins/bss.rb +3 -4
- data/lib/aspera/cli/plugins/config.rb +250 -272
- data/lib/aspera/cli/plugins/console.rb +8 -6
- data/lib/aspera/cli/plugins/cos.rb +20 -19
- data/lib/aspera/cli/plugins/faspex.rb +71 -60
- data/lib/aspera/cli/plugins/faspex5.rb +212 -133
- data/lib/aspera/cli/plugins/node.rb +83 -75
- data/lib/aspera/cli/plugins/orchestrator.rb +36 -44
- data/lib/aspera/cli/plugins/preview.rb +33 -31
- data/lib/aspera/cli/plugins/server.rb +33 -32
- data/lib/aspera/cli/plugins/shares.rb +39 -33
- data/lib/aspera/cli/sync_actions.rb +9 -9
- data/lib/aspera/cli/transfer_agent.rb +45 -25
- data/lib/aspera/cli/transfer_progress.rb +2 -3
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/colors.rb +5 -0
- data/lib/aspera/command_line_builder.rb +16 -14
- data/lib/aspera/coverage.rb +21 -0
- data/lib/aspera/data_repository.rb +33 -2
- data/lib/aspera/environment.rb +5 -4
- data/lib/aspera/faspex_gw.rb +13 -11
- data/lib/aspera/faspex_postproc.rb +6 -5
- data/lib/aspera/id_generator.rb +4 -2
- data/lib/aspera/json_rpc.rb +10 -8
- data/lib/aspera/keychain/encrypted_hash.rb +46 -11
- data/lib/aspera/keychain/macos_security.rb +29 -22
- data/lib/aspera/log.rb +5 -4
- data/lib/aspera/nagios.rb +7 -2
- data/lib/aspera/node_simulator.rb +213 -0
- data/lib/aspera/oauth/base.rb +143 -0
- data/lib/aspera/oauth/factory.rb +124 -0
- data/lib/aspera/oauth/generic.rb +34 -0
- data/lib/aspera/oauth/jwt.rb +51 -0
- data/lib/aspera/oauth/url_json.rb +31 -0
- data/lib/aspera/oauth/web.rb +50 -0
- data/lib/aspera/oauth.rb +5 -328
- data/lib/aspera/open_application.rb +7 -7
- data/lib/aspera/persistency_action_once.rb +13 -14
- data/lib/aspera/persistency_folder.rb +3 -2
- data/lib/aspera/preview/file_types.rb +53 -267
- data/lib/aspera/preview/generator.rb +7 -5
- data/lib/aspera/preview/terminal.rb +17 -7
- data/lib/aspera/preview/utils.rb +8 -7
- data/lib/aspera/proxy_auto_config.rb +6 -3
- data/lib/aspera/rest.rb +187 -140
- data/lib/aspera/rest_error_analyzer.rb +1 -0
- data/lib/aspera/rest_errors_aspera.rb +5 -3
- data/lib/aspera/resumer.rb +77 -0
- data/lib/aspera/secret_hider.rb +5 -2
- data/lib/aspera/ssh.rb +15 -8
- data/lib/aspera/temp_file_manager.rb +1 -1
- data/lib/aspera/{fasp → transfer}/error.rb +3 -3
- data/lib/aspera/{fasp → transfer}/error_info.rb +1 -1
- data/lib/aspera/{fasp → transfer}/faux_file.rb +1 -1
- data/lib/aspera/{fasp → transfer}/parameters.rb +95 -120
- data/lib/aspera/{fasp/transfer_spec.rb → transfer/spec.rb} +23 -19
- data/lib/aspera/{fasp/parameters.yaml → transfer/spec.yaml} +4 -99
- data/lib/aspera/transfer/sync.rb +273 -0
- data/lib/aspera/{fasp → transfer}/uri.rb +10 -9
- data/lib/aspera/web_server_simple.rb +12 -3
- data.tar.gz.sig +0 -0
- metadata +92 -68
- metadata.gz.sig +0 -0
- data/lib/aspera/aoc.rb +0 -606
- data/lib/aspera/ats_api.rb +0 -47
- data/lib/aspera/cos_node.rb +0 -93
- data/lib/aspera/fasp/agent_aspera.rb +0 -126
- data/lib/aspera/fasp/agent_base.rb +0 -48
- data/lib/aspera/fasp/agent_trsdk.rb +0 -146
- data/lib/aspera/fasp/resume_policy.rb +0 -77
- data/lib/aspera/node.rb +0 -338
- data/lib/aspera/sync.rb +0 -219
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Aspera
|
|
4
|
-
module
|
|
5
|
-
#
|
|
4
|
+
module Ascp
|
|
5
|
+
# processing of ascp management port events
|
|
6
6
|
class Management
|
|
7
7
|
# cspell: disable
|
|
8
8
|
OPERATIONS = %w[
|
|
@@ -185,6 +185,8 @@ module Aspera
|
|
|
185
185
|
ExtraCreatePolicy]
|
|
186
186
|
# Management port start message
|
|
187
187
|
MGT_HEADER = 'FASPMGR 2'
|
|
188
|
+
# empty line is separator to end event information
|
|
189
|
+
MGT_FRAME_SEPARATOR = ''
|
|
188
190
|
# fields description for JSON generation
|
|
189
191
|
# spellchecker: disable
|
|
190
192
|
INTEGER_FIELDS = %w[Bytescont FaspFileArgIndex StartByte Rate MinRate Port Priority RateCap MinRateCap TCPPort CreatePolicy TimePolicy
|
|
@@ -193,28 +195,26 @@ module Aspera
|
|
|
193
195
|
ArgScansCompleted PathScansAttempted FileScansCompleted TransfersAttempted TransfersPassed Delay].freeze
|
|
194
196
|
BOOLEAN_FIELDS = %w[Encryption Remote RateLock MinRateLock PolicyLock FilesEncrypt FilesDecrypt VLinkLocalEnabled VLinkRemoteEnabled
|
|
195
197
|
MoveRange Keepalive TestLogin UseProxy Precalc RTTAutocorrect].freeze
|
|
198
|
+
BOOLEAN_TRUE = 'Yes'
|
|
196
199
|
# cspell: enable
|
|
197
200
|
|
|
198
201
|
class << self
|
|
199
|
-
# translates
|
|
202
|
+
# translates mgt port event into (enhanced) typed event
|
|
200
203
|
def enhanced_event_format(event)
|
|
201
204
|
return event.keys.each_with_object({}) do |e, h|
|
|
202
|
-
|
|
203
|
-
new_name = e
|
|
204
|
-
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
|
205
|
-
.gsub(/([a-z\d])(usec)$/, '\1_\2')
|
|
206
|
-
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
|
207
|
-
.downcase
|
|
205
|
+
new_name = e.capital_to_snake.gsub(/(usec)$/, '_\1').downcase
|
|
208
206
|
value = event[e]
|
|
209
207
|
value = value.to_i if INTEGER_FIELDS.include?(e)
|
|
210
|
-
value = value.eql?(
|
|
208
|
+
value = value.eql?(BOOLEAN_TRUE) if BOOLEAN_FIELDS.include?(e)
|
|
211
209
|
h[new_name] = value
|
|
212
210
|
end
|
|
213
211
|
end
|
|
214
212
|
end # class << self
|
|
215
213
|
|
|
216
214
|
def initialize
|
|
215
|
+
# current event being parsed line by line
|
|
217
216
|
@event_build = nil
|
|
217
|
+
# last fully built event
|
|
218
218
|
@last_event = nil
|
|
219
219
|
end
|
|
220
220
|
attr_reader :last_event
|
|
@@ -226,16 +226,16 @@ module Aspera
|
|
|
226
226
|
# begin event
|
|
227
227
|
@event_build = {}
|
|
228
228
|
when /^([^:]+): (.*)$/
|
|
229
|
+
raise 'mgt port: unexpected line: data without header' if @event_build.nil?
|
|
229
230
|
# event field
|
|
230
231
|
@event_build[Regexp.last_match(1)] = Regexp.last_match(2)
|
|
231
|
-
when
|
|
232
|
-
|
|
233
|
-
raise 'unexpected empty line' if @event_build.nil?
|
|
232
|
+
when MGT_FRAME_SEPARATOR
|
|
233
|
+
raise 'mgt port: unexpected line: end frame without header' if @event_build.nil?
|
|
234
234
|
@last_event = @event_build
|
|
235
235
|
@event_build = nil
|
|
236
236
|
return @last_event
|
|
237
237
|
else
|
|
238
|
-
raise "unexpected line:[#{line}]"
|
|
238
|
+
raise "mgt port: unexpected line: [#{line}]"
|
|
239
239
|
end # case
|
|
240
240
|
return nil
|
|
241
241
|
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Aspera
|
|
4
|
+
class InternalError < StandardError
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
class AssertError < StandardError
|
|
8
|
+
end
|
|
9
|
+
class << self
|
|
10
|
+
# the block is executed in the context of the Aspera module
|
|
11
|
+
def assert(assertion, info = nil, level: 2, exception_class: AssertError)
|
|
12
|
+
raise InternalError, 'bad assert: both info and block given' unless info.nil? || !block_given?
|
|
13
|
+
return if assertion
|
|
14
|
+
message = 'assertion failed'
|
|
15
|
+
info = yield if block_given?
|
|
16
|
+
message = "#{message}: #{info}" if info
|
|
17
|
+
message = "#{message}: #{caller(level..level).first}"
|
|
18
|
+
raise exception_class, message
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# assert that value has the given type
|
|
22
|
+
# @param value [Object] the value to check
|
|
23
|
+
# @param type [Class] the expected type
|
|
24
|
+
def assert_type(value, type, exception_class: AssertError)
|
|
25
|
+
assert(value.is_a?(type), level: 3, exception_class: exception_class){"#{block_given? ? "#{yield}: " : nil}expecting #{type}, but have #{value.inspect}"}
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# assert that value is one of the given values
|
|
29
|
+
def assert_values(value, values, exception_class: AssertError)
|
|
30
|
+
assert(values.include?(value), level: 3, exception_class: exception_class) do
|
|
31
|
+
"#{block_given? ? "#{yield}: " : nil}expecting one of #{values.inspect}, but have #{value.inspect}"
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# the line with this shall never be reached
|
|
36
|
+
def error_unreachable_line
|
|
37
|
+
raise InternalError, "unreachable line reached: #{caller(2..2).first}"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# the value is not one of the expected values
|
|
41
|
+
def error_unexpected_value(value, exception_class: InternalError)
|
|
42
|
+
raise exception_class, "#{block_given? ? "#{yield}: " : nil}unexpected value: #{value.inspect}"
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -6,22 +6,23 @@ require 'aspera/cli/plugin'
|
|
|
6
6
|
module Aspera
|
|
7
7
|
module Cli
|
|
8
8
|
# base class for applications supporting basic authentication
|
|
9
|
-
class BasicAuthPlugin <
|
|
9
|
+
class BasicAuthPlugin < Cli::Plugin
|
|
10
10
|
class << self
|
|
11
|
-
|
|
12
|
-
def declare_options(options, force: false
|
|
13
|
-
return if @@basic_options_declared && !force
|
|
14
|
-
|
|
11
|
+
#@@basic_options_declared = false # rubocop:disable Style/ClassVars
|
|
12
|
+
def declare_options(options) # , force: false
|
|
13
|
+
#return if @@basic_options_declared && !force
|
|
14
|
+
#@@basic_options_declared = true # rubocop:disable Style/ClassVars
|
|
15
15
|
options.declare(:url, 'URL of application, e.g. https://faspex.example.com/aspera/faspex')
|
|
16
|
-
options.declare(:username, '
|
|
16
|
+
options.declare(:username, "User's name to log in")
|
|
17
17
|
options.declare(:password, "User's password")
|
|
18
18
|
options.parse_options!
|
|
19
19
|
end
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
def initialize(env)
|
|
23
|
-
super(env)
|
|
24
|
-
|
|
22
|
+
def initialize(basic_options: true, **env)
|
|
23
|
+
super(**env)
|
|
24
|
+
# , force: env[:all_manuals]
|
|
25
|
+
BasicAuthPlugin.declare_options(options) if basic_options
|
|
25
26
|
end
|
|
26
27
|
|
|
27
28
|
# returns a Rest object with basic auth
|
|
@@ -38,7 +39,7 @@ module Aspera
|
|
|
38
39
|
end
|
|
39
40
|
|
|
40
41
|
def basic_auth_api(subpath=nil)
|
|
41
|
-
return Rest.new(basic_auth_params(subpath))
|
|
42
|
+
return Rest.new(**basic_auth_params(subpath))
|
|
42
43
|
end
|
|
43
44
|
end # BasicAuthPlugin
|
|
44
45
|
end # Cli
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
require 'aspera/uri_reader'
|
|
5
5
|
require 'aspera/environment'
|
|
6
6
|
require 'aspera/log'
|
|
7
|
+
require 'aspera/assert'
|
|
7
8
|
require 'json'
|
|
8
9
|
require 'base64'
|
|
9
10
|
require 'zlib'
|
|
@@ -17,6 +18,7 @@ module Aspera
|
|
|
17
18
|
include Singleton
|
|
18
19
|
|
|
19
20
|
# special values
|
|
21
|
+
INIT = 'INIT'
|
|
20
22
|
ALL = 'ALL'
|
|
21
23
|
DEF = 'DEF'
|
|
22
24
|
|
|
@@ -30,9 +32,7 @@ module Aspera
|
|
|
30
32
|
if col_titles.nil?
|
|
31
33
|
col_titles = values
|
|
32
34
|
else
|
|
33
|
-
|
|
34
|
-
col_titles.each{|title|entry[title] = values.shift}
|
|
35
|
-
hash_array.push(entry)
|
|
35
|
+
hash_array.push(col_titles.zip(values).to_h)
|
|
36
36
|
end
|
|
37
37
|
end
|
|
38
38
|
Log.log.warn('Titled CSV file without any line') if hash_array.empty?
|
|
@@ -63,7 +63,7 @@ module Aspera
|
|
|
63
63
|
path: lambda{|v|File.expand_path(v)},
|
|
64
64
|
re: lambda{|v|Regexp.new(v)},
|
|
65
65
|
ruby: lambda{|v|Environment.secure_eval(v, __FILE__, __LINE__)},
|
|
66
|
-
secret: lambda{|v|
|
|
66
|
+
secret: lambda{|v|prompt = v.empty? ? 'secret' : v; $stdin.getpass("#{prompt}> ")}, # rubocop:disable Style/Semicolon
|
|
67
67
|
stdin: lambda{|v|ExtendedValue.assert_no_value(v, :stdin); $stdin.read}, # rubocop:disable Style/Semicolon
|
|
68
68
|
yaml: lambda{|v|YAML.load(v)},
|
|
69
69
|
zlib: lambda{|v|Zlib::Inflate.inflate(v)},
|
|
@@ -78,7 +78,7 @@ module Aspera
|
|
|
78
78
|
# add a new handler
|
|
79
79
|
def set_handler(name, method)
|
|
80
80
|
Log.log.debug{"setting handler for #{name}"}
|
|
81
|
-
|
|
81
|
+
Aspera.assert_type(name, Symbol){'name'}
|
|
82
82
|
@handlers[name] = method
|
|
83
83
|
end
|
|
84
84
|
|
data/lib/aspera/cli/formatter.rb
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
# cspell:ignore jsonpp
|
|
4
4
|
require 'aspera/secret_hider'
|
|
5
5
|
require 'aspera/environment'
|
|
6
|
+
require 'aspera/log'
|
|
7
|
+
require 'aspera/assert'
|
|
6
8
|
require 'terminal-table'
|
|
7
9
|
require 'yaml'
|
|
8
10
|
require 'pp'
|
|
@@ -18,7 +20,7 @@ module Aspera
|
|
|
18
20
|
|
|
19
21
|
# General method
|
|
20
22
|
def flatten(something)
|
|
21
|
-
|
|
23
|
+
Aspera.assert_type(something, Hash)
|
|
22
24
|
@result = {}
|
|
23
25
|
flatten_any(something, '')
|
|
24
26
|
return @result
|
|
@@ -94,6 +96,7 @@ module Aspera
|
|
|
94
96
|
DISPLAY_FORMATS = %i[text nagios ruby json jsonpp yaml table csv].freeze
|
|
95
97
|
# user output levels
|
|
96
98
|
DISPLAY_LEVELS = %i[info data error].freeze
|
|
99
|
+
RESULT_PARAMS = %i[type data total fields name].freeze
|
|
97
100
|
|
|
98
101
|
private_constant :DISPLAY_FORMATS, :DISPLAY_LEVELS, :CSV_RECORD_SEPARATOR, :CSV_FIELD_SEPARATOR
|
|
99
102
|
# prefix to display error messages in user messages (terminal)
|
|
@@ -104,7 +107,7 @@ module Aspera
|
|
|
104
107
|
class << self
|
|
105
108
|
# Highlight special values
|
|
106
109
|
def special(what, use_colors: $stdout.isatty)
|
|
107
|
-
result = "<#{what}>"
|
|
110
|
+
result = $stdout.isatty ? "<#{what}>" : "<#{what}>"
|
|
108
111
|
if use_colors
|
|
109
112
|
result = if %w[null empty].any?{|s|what.include?(s)}
|
|
110
113
|
result.dim
|
|
@@ -155,16 +158,26 @@ module Aspera
|
|
|
155
158
|
end
|
|
156
159
|
|
|
157
160
|
def option_handler(option_symbol, operation, value=nil)
|
|
161
|
+
Aspera.assert_values(operation, %i[set get])
|
|
158
162
|
case operation
|
|
159
|
-
when :set
|
|
163
|
+
when :set
|
|
164
|
+
@options[option_symbol] = value
|
|
165
|
+
if option_symbol.eql?(:output)
|
|
166
|
+
$stdout = if value.eql?('-')
|
|
167
|
+
STDOUT # rubocop:disable Style/GlobalStdStream
|
|
168
|
+
else
|
|
169
|
+
File.open(value, 'w')
|
|
170
|
+
end
|
|
171
|
+
end
|
|
160
172
|
when :get then return @options[option_symbol]
|
|
161
|
-
else
|
|
173
|
+
else Aspera.error_unreachable_line
|
|
162
174
|
end
|
|
163
175
|
nil
|
|
164
176
|
end
|
|
165
177
|
|
|
166
178
|
def declare_options(options)
|
|
167
179
|
options.declare(:format, 'Output format', values: DISPLAY_FORMATS, handler: {o: self, m: :option_handler}, default: :table)
|
|
180
|
+
options.declare(:output, 'Destination for results', types: String, handler: {o: self, m: :option_handler})
|
|
168
181
|
options.declare(:display, 'Output only some information', values: DISPLAY_LEVELS, handler: {o: self, m: :option_handler}, default: :info)
|
|
169
182
|
options.declare(
|
|
170
183
|
:fields, "Comma separated list of: fields, or #{ExtendedValue::ALL}, or #{ExtendedValue::DEF}", handler: {o: self, m: :option_handler},
|
|
@@ -186,7 +199,7 @@ module Aspera
|
|
|
186
199
|
when :data then $stdout.puts(message) unless @options[:display].eql?(:error)
|
|
187
200
|
when :info then $stdout.puts(message) if @options[:display].eql?(:info)
|
|
188
201
|
when :error then $stderr.puts(message)
|
|
189
|
-
else
|
|
202
|
+
else Aspera.error_unexpected_value(message_level)
|
|
190
203
|
end
|
|
191
204
|
end
|
|
192
205
|
|
|
@@ -219,7 +232,7 @@ module Aspera
|
|
|
219
232
|
when Array then @options[:fields]
|
|
220
233
|
when Regexp then return all_fields(data).select{|i|i.match(@options[:fields])}
|
|
221
234
|
when Proc then return all_fields(data).select{|i|@options[:fields].call(i)}
|
|
222
|
-
else
|
|
235
|
+
else Aspera.error_unexpected_value(@options[:fields])
|
|
223
236
|
end
|
|
224
237
|
result = []
|
|
225
238
|
until request.empty?
|
|
@@ -252,7 +265,7 @@ module Aspera
|
|
|
252
265
|
# object_array: array of hash
|
|
253
266
|
# fields: list of column names
|
|
254
267
|
def display_table(object_array, fields)
|
|
255
|
-
|
|
268
|
+
Aspera.assert(!fields.nil?){'missing fields parameter'}
|
|
256
269
|
case @options[:select]
|
|
257
270
|
when Proc
|
|
258
271
|
object_array.select!{|i|@options[:select].call(i)}
|
|
@@ -296,12 +309,12 @@ module Aspera
|
|
|
296
309
|
|
|
297
310
|
# this method displays the results, especially the table format
|
|
298
311
|
def display_results(results)
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
raise 'INTERNAL ERROR, result must have data' unless results.key?(:data) || %i[empty nothing].include?(results[:type])
|
|
312
|
+
Aspera.assert_type(results, Hash)
|
|
313
|
+
Aspera.assert((results.keys - RESULT_PARAMS).empty?){"result unsupported key: #{results.keys - RESULT_PARAMS}"}
|
|
314
|
+
Aspera.assert(results.key?(:type)){"result must have type (#{results})"}
|
|
315
|
+
Aspera.assert(results.key?(:data) || %i[empty nothing].include?(results[:type])){'result must have data'}
|
|
304
316
|
Log.log.debug{"display_results: #{results[:data].class} #{results[:type]}"}
|
|
317
|
+
display_item_count(results[:data].length, results[:total]) if results.key?(:total)
|
|
305
318
|
SecretHider.deep_remove_secret(results[:data]) unless @options[:show_secrets] || @options[:display].eql?(:data)
|
|
306
319
|
case @options[:format]
|
|
307
320
|
when :text
|
|
@@ -323,8 +336,8 @@ module Aspera
|
|
|
323
336
|
when :object_list, :single_object
|
|
324
337
|
obj_list = results[:data]
|
|
325
338
|
obj_list = [obj_list] if results[:type].eql?(:single_object)
|
|
326
|
-
|
|
327
|
-
|
|
339
|
+
Aspera.assert_type(obj_list, Array)
|
|
340
|
+
Aspera.assert(obj_list.all?(Hash)){"expecting Array of Hash: #{obj_list.inspect}"}
|
|
328
341
|
# :object_list is an array of hash tables, where key=colum name
|
|
329
342
|
obj_list = obj_list.map{|obj|Flattener.new.flatten(obj)} if @options[:flat_hash]
|
|
330
343
|
display_table(obj_list, compute_fields(obj_list, results[:fields]))
|
data/lib/aspera/cli/hints.rb
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'aspera/
|
|
3
|
+
require 'aspera/transfer/error'
|
|
4
4
|
require 'aspera/rest'
|
|
5
|
+
require 'aspera/log'
|
|
6
|
+
require 'aspera/assert'
|
|
5
7
|
require 'net/ssh'
|
|
6
8
|
require 'openssl'
|
|
7
9
|
|
|
@@ -12,7 +14,7 @@ module Aspera
|
|
|
12
14
|
# Well know issues that users may get
|
|
13
15
|
ERROR_HINTS = [
|
|
14
16
|
{
|
|
15
|
-
exception:
|
|
17
|
+
exception: Transfer::Error,
|
|
16
18
|
match: 'Remote host is not who we expected',
|
|
17
19
|
remediation: [
|
|
18
20
|
'For this specific error, refer to:',
|
|
@@ -22,7 +24,7 @@ module Aspera
|
|
|
22
24
|
]
|
|
23
25
|
},
|
|
24
26
|
{
|
|
25
|
-
exception:
|
|
27
|
+
exception: RestCallError,
|
|
26
28
|
match: /Signature has expired/,
|
|
27
29
|
remediation: [
|
|
28
30
|
'There is too much time difference between your computer and the server',
|
|
@@ -58,14 +60,13 @@ module Aspera
|
|
|
58
60
|
matches = hint[:match]
|
|
59
61
|
matches = [matches] unless matches.is_a?(Array)
|
|
60
62
|
matches.each do |m|
|
|
63
|
+
Aspera.assert_values(m.class, [String, Regexp])
|
|
61
64
|
case m
|
|
62
65
|
when String
|
|
63
66
|
next unless message.eql?(m)
|
|
64
67
|
when Regexp
|
|
65
68
|
next unless message.match?(m)
|
|
66
|
-
else
|
|
67
|
-
Log.log.warn("Internal error: hint is a #{m.class}")
|
|
68
|
-
next
|
|
69
|
+
else Aspera.error_unexpected_value(m)
|
|
69
70
|
end
|
|
70
71
|
remediation = hint[:remediation]
|
|
71
72
|
remediation = [remediation] unless remediation.is_a?(Array)
|
data/lib/aspera/cli/main.rb
CHANGED
|
@@ -4,12 +4,15 @@ require 'aspera/cli/manager'
|
|
|
4
4
|
require 'aspera/cli/formatter'
|
|
5
5
|
require 'aspera/cli/plugins/config'
|
|
6
6
|
require 'aspera/cli/extended_value'
|
|
7
|
+
require 'aspera/cli/plugin_factory'
|
|
7
8
|
require 'aspera/cli/transfer_agent'
|
|
8
9
|
require 'aspera/cli/version'
|
|
9
10
|
require 'aspera/cli/info'
|
|
10
11
|
require 'aspera/cli/hints'
|
|
12
|
+
require 'aspera/preview/terminal'
|
|
11
13
|
require 'aspera/secret_hider'
|
|
12
14
|
require 'aspera/log'
|
|
15
|
+
require 'aspera/assert'
|
|
13
16
|
|
|
14
17
|
module Aspera
|
|
15
18
|
module Cli
|
|
@@ -17,6 +20,9 @@ module Aspera
|
|
|
17
20
|
class Main
|
|
18
21
|
# Plugins store transfer result using this key and use result_transfer_multiple()
|
|
19
22
|
STATUS_FIELD = 'status'
|
|
23
|
+
CONF_PLUGIN_SYM = :config
|
|
24
|
+
|
|
25
|
+
private_constant :CONF_PLUGIN_SYM
|
|
20
26
|
|
|
21
27
|
class << self
|
|
22
28
|
# expect some list, but nothing to display
|
|
@@ -53,13 +59,21 @@ module Aspera
|
|
|
53
59
|
raise global_status unless global_status.eql?(:success)
|
|
54
60
|
return {type: :object_list, data: status_table}
|
|
55
61
|
end
|
|
62
|
+
|
|
63
|
+
def result_picture_in_terminal(options, blob)
|
|
64
|
+
terminal_options = options.get_option(:query, default: {}).symbolize_keys
|
|
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))
|
|
69
|
+
end
|
|
56
70
|
end # self
|
|
57
71
|
|
|
58
72
|
private
|
|
59
73
|
|
|
60
74
|
# shortcuts helpers like in plugins
|
|
61
75
|
%i[options transfer config formatter persistency].each do |name|
|
|
62
|
-
define_method(name){@
|
|
76
|
+
define_method(name){@plug_init[name]}
|
|
63
77
|
end
|
|
64
78
|
|
|
65
79
|
# =============================================================
|
|
@@ -70,7 +84,7 @@ module Aspera
|
|
|
70
84
|
def initialize(argv)
|
|
71
85
|
@argv = argv
|
|
72
86
|
# environment provided to plugin for various capabilities
|
|
73
|
-
@
|
|
87
|
+
@plug_init = Plugin::INIT_PARAMS.each_with_object({}) { |key, hash| hash[key] = nil }
|
|
74
88
|
@option_help = false
|
|
75
89
|
@option_show_config = false
|
|
76
90
|
@bash_completion = false
|
|
@@ -78,10 +92,12 @@ module Aspera
|
|
|
78
92
|
|
|
79
93
|
# This can throw exception if there is a problem with the environment, needs to be caught by execute method
|
|
80
94
|
def init_agents_and_options
|
|
81
|
-
|
|
95
|
+
@plug_init[:only_manual] = false
|
|
96
|
+
# create formatter, in case there is an exception, it is used to display.
|
|
97
|
+
@plug_init[:formatter] = Formatter.new
|
|
98
|
+
# second : manage debug level (allows debugging of option parser)
|
|
82
99
|
early_debug_setup
|
|
83
|
-
@
|
|
84
|
-
@agents[:options] = Manager.new(PROGRAM_NAME)
|
|
100
|
+
@plug_init[:options] = Manager.new(PROGRAM_NAME)
|
|
85
101
|
# give command line arguments to option manager
|
|
86
102
|
options.parse_command_line(@argv)
|
|
87
103
|
# formatter adds options
|
|
@@ -94,11 +110,14 @@ module Aspera
|
|
|
94
110
|
# declare and parse global options
|
|
95
111
|
declare_global_options
|
|
96
112
|
# the Config plugin adds the @preset parser, so declare before TransferAgent which may use it
|
|
97
|
-
@
|
|
113
|
+
@plug_init[:config] = Plugins::Config.new(**@plug_init, gem: GEM_NAME, name: PROGRAM_NAME, help: DOC_URL, version: Cli::VERSION)
|
|
114
|
+
@plug_init[:persistency] = @plug_init[:config].persistency
|
|
98
115
|
# data persistency
|
|
99
|
-
|
|
116
|
+
Aspera.assert(@plug_init[:persistency]){'missing persistency object'}
|
|
100
117
|
# the TransferAgent plugin may use the @preset parser
|
|
101
|
-
@
|
|
118
|
+
@plug_init[:config].transfer = @plug_init[:transfer] = TransferAgent.new(options, config)
|
|
119
|
+
nil_keys = @plug_init.select{|_, value|value.nil?}.keys
|
|
120
|
+
Aspera.assert(nil_keys.empty?){"nil : #{nil_keys}"}
|
|
102
121
|
Log.log.debug('plugin env created'.red)
|
|
103
122
|
# set banner when all environment is created so that additional extended value modifiers are known, e.g. @preset
|
|
104
123
|
options.parser.banner = app_banner
|
|
@@ -108,7 +127,7 @@ module Aspera
|
|
|
108
127
|
t = ' ' * 8
|
|
109
128
|
return <<~END_OF_BANNER
|
|
110
129
|
NAME
|
|
111
|
-
#{t}#{PROGRAM_NAME} -- a command line tool for Aspera Applications (v#{
|
|
130
|
+
#{t}#{PROGRAM_NAME} -- a command line tool for Aspera Applications (v#{Cli::VERSION})
|
|
112
131
|
|
|
113
132
|
SYNOPSIS
|
|
114
133
|
#{t}#{PROGRAM_NAME} COMMANDS [OPTIONS] [ARGS]
|
|
@@ -144,7 +163,7 @@ module Aspera
|
|
|
144
163
|
options.declare(:help, 'Show this message', values: :none, short: 'h') { @option_help = true }
|
|
145
164
|
options.declare(:bash_comp, 'Generate bash completion for command', values: :none) { @bash_completion = true }
|
|
146
165
|
options.declare(:show_config, 'Display parameters used for the provided action', values: :none) { @option_show_config = true }
|
|
147
|
-
options.declare(:version, 'Display version', values: :none, short: 'v') { formatter.display_message(:data,
|
|
166
|
+
options.declare(:version, 'Display version', values: :none, short: 'v') { formatter.display_message(:data, Cli::VERSION); Process.exit(0) } # rubocop:disable Style/Semicolon, Layout/LineLength
|
|
148
167
|
options.declare(:warnings, 'Check for language warnings', values: :none, short: 'w') { $VERBOSE = true }
|
|
149
168
|
options.declare(
|
|
150
169
|
:ui, 'Method to start browser',
|
|
@@ -166,20 +185,19 @@ module Aspera
|
|
|
166
185
|
# also loads the plugin options, and default values from conf file
|
|
167
186
|
# @param plugin_name_sym : symbol for plugin name
|
|
168
187
|
def get_plugin_instance_with_options(plugin_name_sym, env=nil)
|
|
169
|
-
env ||= @
|
|
188
|
+
env ||= @plug_init
|
|
170
189
|
Log.log.debug{"get_plugin_instance_with_options(#{plugin_name_sym})"}
|
|
171
|
-
require
|
|
190
|
+
require PluginFactory.instance.plugins[plugin_name_sym][:require_stanza]
|
|
172
191
|
# load default params only if no param already loaded before plugin instantiation
|
|
173
192
|
env[:config].add_plugin_default_preset(plugin_name_sym)
|
|
174
|
-
command_plugin =
|
|
193
|
+
command_plugin = PluginFactory.instance.create(plugin_name_sym, **env)
|
|
175
194
|
Log.log.debug{"got #{command_plugin.class}"}
|
|
176
|
-
# TODO: check that ancestor is Plugin?
|
|
177
195
|
return command_plugin
|
|
178
196
|
end
|
|
179
197
|
|
|
180
198
|
def generate_bash_completion
|
|
181
199
|
if options.get_next_argument('', expected: :multiple, mandatory: false).nil?
|
|
182
|
-
|
|
200
|
+
PluginFactory.instance.plugins.each_key{|p|puts p}
|
|
183
201
|
else
|
|
184
202
|
Log.log.warn('only first level completion so far')
|
|
185
203
|
end
|
|
@@ -192,11 +210,11 @@ module Aspera
|
|
|
192
210
|
formatter.display_message(:error, options.parser)
|
|
193
211
|
if all_plugins
|
|
194
212
|
# list plugins that have a "require" field, i.e. all but main plugin
|
|
195
|
-
|
|
196
|
-
next if plugin_name_sym.eql?(
|
|
213
|
+
PluginFactory.instance.plugins.each_key do |plugin_name_sym|
|
|
214
|
+
next if plugin_name_sym.eql?(CONF_PLUGIN_SYM)
|
|
197
215
|
# override main option parser with a brand new, to avoid having global options
|
|
198
|
-
plugin_env = @
|
|
199
|
-
plugin_env[:
|
|
216
|
+
plugin_env = @plug_init.clone
|
|
217
|
+
plugin_env[:only_manual] = true # force declaration of all options
|
|
200
218
|
plugin_env[:options] = Manager.new(PROGRAM_NAME)
|
|
201
219
|
plugin_env[:options].parser.banner = '' # remove default banner
|
|
202
220
|
get_plugin_instance_with_options(plugin_name_sym, plugin_env)
|
|
@@ -212,13 +230,15 @@ module Aspera
|
|
|
212
230
|
# early debug for parser
|
|
213
231
|
# Note: does not accept shortcuts
|
|
214
232
|
def early_debug_setup
|
|
215
|
-
|
|
233
|
+
Log.instance.program_name = PROGRAM_NAME
|
|
216
234
|
@argv.each do |arg|
|
|
217
235
|
case arg
|
|
218
236
|
when '--' then break
|
|
219
|
-
when /^--log-level=(.*)/ then
|
|
220
|
-
when /^--logger=(.*)/ then
|
|
237
|
+
when /^--log-level=(.*)/ then Log.instance.level = Regexp.last_match(1).to_sym
|
|
238
|
+
when /^--logger=(.*)/ then Log.instance.logger_type = Regexp.last_match(1).to_sym
|
|
221
239
|
end
|
|
240
|
+
rescue => e
|
|
241
|
+
$stderr.puts("Error: #{e}")
|
|
222
242
|
end
|
|
223
243
|
end
|
|
224
244
|
|
|
@@ -234,16 +254,16 @@ module Aspera
|
|
|
234
254
|
begin
|
|
235
255
|
init_agents_and_options
|
|
236
256
|
# find plugins, shall be after parse! ?
|
|
237
|
-
|
|
257
|
+
PluginFactory.instance.add_plugins_from_lookup_folders
|
|
238
258
|
# help requested without command ? (plugins must be known here)
|
|
239
259
|
exit_with_usage(true) if @option_help && options.command_or_arg_empty?
|
|
240
260
|
generate_bash_completion if @bash_completion
|
|
241
261
|
config.periodic_check_newer_gem_version
|
|
242
262
|
command_sym =
|
|
243
263
|
if @option_show_config && options.command_or_arg_empty?
|
|
244
|
-
|
|
264
|
+
CONF_PLUGIN_SYM
|
|
245
265
|
else
|
|
246
|
-
options.get_next_command(
|
|
266
|
+
options.get_next_command(PluginFactory.instance.plugins.keys.dup.unshift(:help))
|
|
247
267
|
end
|
|
248
268
|
# command will not be executed, but we need manual
|
|
249
269
|
options.fail_on_missing_mandatory = false if @option_help || @option_show_config
|
|
@@ -251,7 +271,7 @@ module Aspera
|
|
|
251
271
|
case command_sym
|
|
252
272
|
when :help
|
|
253
273
|
exit_with_usage(true)
|
|
254
|
-
when
|
|
274
|
+
when CONF_PLUGIN_SYM
|
|
255
275
|
command_plugin = config
|
|
256
276
|
else
|
|
257
277
|
# get plugin, set options, etc
|
|
@@ -294,8 +314,8 @@ module Aspera
|
|
|
294
314
|
rescue Cli::BadArgument => e; exception_info = {e: e, t: 'Argument', usage: true}
|
|
295
315
|
rescue Cli::NoSuchIdentifier => e; exception_info = {e: e, t: 'Identifier'}
|
|
296
316
|
rescue Cli::Error => e; exception_info = {e: e, t: 'Tool', usage: true}
|
|
297
|
-
rescue
|
|
298
|
-
rescue
|
|
317
|
+
rescue Transfer::Error => e; exception_info = {e: e, t: 'Transfer'}
|
|
318
|
+
rescue RestCallError => e; exception_info = {e: e, t: 'Rest'}
|
|
299
319
|
rescue SocketError => e; exception_info = {e: e, t: 'Network'}
|
|
300
320
|
rescue StandardError => e; exception_info = {e: e, t: "Other(#{e.class.name})", debug: true}
|
|
301
321
|
rescue Interrupt => e; exception_info = {e: e, t: 'Interruption', debug: true}
|
|
@@ -304,7 +324,7 @@ module Aspera
|
|
|
304
324
|
TempFileManager.instance.cleanup
|
|
305
325
|
# 1- processing of error condition
|
|
306
326
|
unless exception_info.nil?
|
|
307
|
-
Log.log.warn(exception_info[:e].message) if
|
|
327
|
+
Log.log.warn(exception_info[:e].message) if Log.instance.logger_type.eql?(:syslog) && exception_info[:security]
|
|
308
328
|
formatter.display_message(:error, "#{Formatter::ERROR_FLASH} #{exception_info[:t]}: #{exception_info[:e].message}")
|
|
309
329
|
formatter.display_message(:error, 'Use option -h to get help.') if exception_info[:usage]
|
|
310
330
|
# Is that a known error condition with proposal for remediation ?
|