aspera-cli 4.15.0 → 4.16.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 +0 -0
- data/BUGS.md +29 -3
- data/CHANGELOG.md +292 -228
- data/CONTRIBUTING.md +69 -18
- data/README.md +1102 -952
- data/bin/ascli +13 -31
- data/bin/asession +3 -1
- data/examples/dascli +2 -2
- data/lib/aspera/aoc.rb +28 -33
- data/lib/aspera/ascmd.rb +3 -6
- data/lib/aspera/assert.rb +45 -0
- data/lib/aspera/cli/extended_value.rb +5 -5
- data/lib/aspera/cli/formatter.rb +26 -13
- data/lib/aspera/cli/hints.rb +4 -3
- data/lib/aspera/cli/main.rb +16 -3
- data/lib/aspera/cli/manager.rb +45 -36
- data/lib/aspera/cli/plugin.rb +20 -13
- data/lib/aspera/cli/plugins/aoc.rb +103 -73
- data/lib/aspera/cli/plugins/ats.rb +4 -3
- data/lib/aspera/cli/plugins/config.rb +114 -119
- data/lib/aspera/cli/plugins/cos.rb +2 -2
- data/lib/aspera/cli/plugins/faspex.rb +23 -19
- data/lib/aspera/cli/plugins/faspex5.rb +75 -43
- data/lib/aspera/cli/plugins/node.rb +28 -15
- data/lib/aspera/cli/plugins/orchestrator.rb +4 -2
- data/lib/aspera/cli/plugins/preview.rb +9 -7
- data/lib/aspera/cli/plugins/server.rb +6 -3
- data/lib/aspera/cli/plugins/shares.rb +30 -26
- data/lib/aspera/cli/sync_actions.rb +9 -9
- data/lib/aspera/cli/transfer_agent.rb +21 -14
- data/lib/aspera/cli/transfer_progress.rb +2 -3
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/command_line_builder.rb +13 -11
- data/lib/aspera/cos_node.rb +3 -2
- data/lib/aspera/coverage.rb +22 -0
- data/lib/aspera/data_repository.rb +33 -2
- data/lib/aspera/environment.rb +4 -2
- data/lib/aspera/fasp/{agent_aspera.rb → agent_alpha.rb} +29 -39
- data/lib/aspera/fasp/agent_base.rb +17 -7
- data/lib/aspera/fasp/agent_direct.rb +88 -84
- data/lib/aspera/fasp/agent_httpgw.rb +4 -3
- data/lib/aspera/fasp/agent_node.rb +3 -2
- data/lib/aspera/fasp/agent_trsdk.rb +79 -37
- data/lib/aspera/fasp/installation.rb +51 -12
- data/lib/aspera/fasp/management.rb +11 -6
- data/lib/aspera/fasp/parameters.rb +53 -47
- data/lib/aspera/fasp/resume_policy.rb +7 -5
- data/lib/aspera/fasp/sync.rb +273 -0
- data/lib/aspera/fasp/transfer_spec.rb +10 -8
- data/lib/aspera/fasp/uri.rb +2 -2
- data/lib/aspera/faspex_gw.rb +11 -8
- data/lib/aspera/faspex_postproc.rb +6 -5
- data/lib/aspera/id_generator.rb +3 -1
- data/lib/aspera/json_rpc.rb +10 -8
- data/lib/aspera/keychain/encrypted_hash.rb +46 -11
- data/lib/aspera/keychain/macos_security.rb +15 -13
- data/lib/aspera/log.rb +4 -3
- data/lib/aspera/nagios.rb +7 -2
- data/lib/aspera/node.rb +17 -16
- data/lib/aspera/node_simulator.rb +214 -0
- data/lib/aspera/oauth.rb +22 -19
- 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 +14 -5
- data/lib/aspera/preview/utils.rb +8 -7
- data/lib/aspera/proxy_auto_config.rb +6 -3
- data/lib/aspera/rest.rb +29 -13
- data/lib/aspera/rest_error_analyzer.rb +1 -0
- data/lib/aspera/rest_errors_aspera.rb +2 -0
- data/lib/aspera/secret_hider.rb +5 -2
- data/lib/aspera/ssh.rb +10 -8
- data/lib/aspera/temp_file_manager.rb +1 -1
- data/lib/aspera/web_server_simple.rb +2 -1
- data.tar.gz.sig +0 -0
- metadata +96 -45
- metadata.gz.sig +0 -0
- data/lib/aspera/sync.rb +0 -219
data/bin/ascli
CHANGED
@@ -3,36 +3,18 @@
|
|
3
3
|
|
4
4
|
Encoding.default_internal = Encoding::UTF_8
|
5
5
|
Encoding.default_external = Encoding::UTF_8
|
6
|
-
|
7
|
-
if ENV.key?('ENABLE_COVERAGE')
|
8
|
-
require 'simplecov'
|
9
|
-
require 'securerandom'
|
10
|
-
# compute gem source root based on this script location, assuming it is in bin/
|
11
|
-
# use dirname instead of gsub, in case folder separator is not /
|
12
|
-
development_root = File.dirname(File.dirname(File.realpath(__FILE__)))
|
13
|
-
SimpleCov.root(development_root)
|
14
|
-
SimpleCov.enable_for_subprocesses if SimpleCov.respond_to?(:enable_for_subprocesses)
|
15
|
-
# keep cache data for 1 day (must be longer that time to run the whole test suite)
|
16
|
-
SimpleCov.merge_timeout(86400)
|
17
|
-
SimpleCov.command_name(SecureRandom.uuid)
|
18
|
-
SimpleCov.at_exit do
|
19
|
-
original_file_descriptor = $stdout
|
20
|
-
$stdout.reopen(File.join(development_root, 'simplecov.log'))
|
21
|
-
SimpleCov.result.format!
|
22
|
-
$stdout.reopen(original_file_descriptor)
|
23
|
-
end
|
24
|
-
SimpleCov.start
|
25
|
-
end
|
26
|
-
# if in development, add path to gem
|
27
|
-
#
|
6
|
+
|
28
7
|
begin
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
8
|
+
gem_lib_folder = File.join(File.dirname(File.dirname(File.realpath(__FILE__))), 'lib')
|
9
|
+
Kernel.load(File.join(gem_lib_folder, 'aspera/coverage.rb'))
|
10
|
+
begin
|
11
|
+
require 'aspera/cli/main'
|
12
|
+
rescue LoadError
|
13
|
+
# if in development, add path toward gem
|
14
|
+
$LOAD_PATH.unshift(gem_lib_folder)
|
15
|
+
require 'aspera/cli/main'
|
16
|
+
end
|
17
|
+
require 'aspera/environment'
|
18
|
+
Aspera::Environment.fix_home
|
19
|
+
Aspera::Cli::Main.new(ARGV).process_command_line
|
35
20
|
end
|
36
|
-
require 'aspera/environment'
|
37
|
-
Aspera::Environment.fix_home
|
38
|
-
Aspera::Cli::Main.new(ARGV).process_command_line
|
data/bin/asession
CHANGED
@@ -75,10 +75,12 @@ client = Aspera::Fasp::AgentDirect.new({quiet: true})
|
|
75
75
|
job_id = client.start_transfer(transfer_spec)
|
76
76
|
# async commands
|
77
77
|
Thread.new do
|
78
|
+
# we assume here a single session
|
79
|
+
session_id = client.sessions_by_job(job_id).first
|
78
80
|
begin # rubocop:disable Style/RedundantBegin
|
79
81
|
loop do
|
80
82
|
data = JSON.parse($stdin.gets)
|
81
|
-
client.send_command(
|
83
|
+
client.send_command(session_id, data)
|
82
84
|
end
|
83
85
|
rescue
|
84
86
|
Process.exit(1)
|
data/examples/dascli
CHANGED
@@ -7,7 +7,7 @@
|
|
7
7
|
: "${imgtag=$image:$version}"
|
8
8
|
# set env var `docker` to podman, to use podman
|
9
9
|
: "${docker:=docker}"
|
10
|
-
# set env var docker_args to add options to docker run (transform var into array) # spellcheck disable=SC2086
|
10
|
+
# set env var `docker_args` to add options to docker run (then, transform this var into array) # spellcheck disable=SC2086
|
11
11
|
read -a add_dock_args <<< $docker_args
|
12
12
|
# set env var ASCLI_HOME to set the config folder on host
|
13
13
|
: "${ASCLI_HOME:=$HOME/.aspera/ascli}"
|
@@ -24,7 +24,7 @@ exec $docker run \
|
|
24
24
|
--interactive \
|
25
25
|
--user root \
|
26
26
|
--env ASCLI_HOME="$ascli_home_container" \
|
27
|
-
--volume "$ASCLI_HOME:$ascli_home_container" \
|
27
|
+
--volume "$ASCLI_HOME:$ascli_home_container:z" \
|
28
28
|
"${add_dock_args[@]}" \
|
29
29
|
"$imgtag" \
|
30
30
|
"$@"
|
data/lib/aspera/aoc.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'aspera/log'
|
4
|
+
require 'aspera/assert'
|
4
5
|
require 'aspera/rest'
|
5
6
|
require 'aspera/hash_ext'
|
6
7
|
require 'aspera/data_repository'
|
@@ -31,10 +32,9 @@ module Aspera
|
|
31
32
|
PROD_DOMAIN = 'ibmaspera.com' # cspell:disable-line
|
32
33
|
# to avoid infinite loop in pub link redirection
|
33
34
|
MAX_AOC_URL_REDIRECT = 10
|
35
|
+
CLIENT_ID_PREFIX = 'aspera.'
|
34
36
|
# Well-known AoC globals client apps
|
35
|
-
GLOBAL_CLIENT_APPS =
|
36
|
-
# index offset in data repository of client app
|
37
|
-
DATA_REPO_INDEX_START = 4
|
37
|
+
GLOBAL_CLIENT_APPS = DataRepository::ELEMENTS.select{|i|i.to_s.start_with?(CLIENT_ID_PREFIX)}.freeze
|
38
38
|
# cookie prefix so that console can decode identity
|
39
39
|
COOKIE_PREFIX_CONSOLE_AOC = 'aspera.aoc'
|
40
40
|
# path in URL of public links
|
@@ -50,7 +50,6 @@ module Aspera
|
|
50
50
|
|
51
51
|
private_constant :MAX_AOC_URL_REDIRECT,
|
52
52
|
:GLOBAL_CLIENT_APPS,
|
53
|
-
:DATA_REPO_INDEX_START,
|
54
53
|
:COOKIE_PREFIX_CONSOLE_AOC,
|
55
54
|
:PUBLIC_LINK_PATHS,
|
56
55
|
:JWT_AUDIENCE,
|
@@ -71,10 +70,9 @@ module Aspera
|
|
71
70
|
# class static methods
|
72
71
|
class << self
|
73
72
|
# strings /Applications/Aspera\ Drive.app/Contents/MacOS/AsperaDrive|grep -E '.{100}==$'|base64 --decode
|
74
|
-
def get_client_info(client_name=
|
75
|
-
|
76
|
-
|
77
|
-
return client_name, Base64.urlsafe_encode64(DataRepository.instance.data(DATA_REPO_INDEX_START + client_index))
|
73
|
+
def get_client_info(client_name=nil)
|
74
|
+
client_key = client_name.nil? ? GLOBAL_CLIENT_APPS.first : client_name.to_sym
|
75
|
+
return client_key, DataRepository.instance.item(client_key)
|
78
76
|
end
|
79
77
|
|
80
78
|
# base API url depends on domain, which could be "qa.xxx"
|
@@ -93,7 +91,7 @@ module Aspera
|
|
93
91
|
def url_parts(uri)
|
94
92
|
raise "No host found in URL.Please check URL format: https://myorg.#{PROD_DOMAIN}" if uri.host.nil?
|
95
93
|
parts = uri.host.split('.', 2)
|
96
|
-
|
94
|
+
assert(parts.length == 2){"expecting a public FQDN for #{PRODUCT_NAME}"}
|
97
95
|
return parts
|
98
96
|
end
|
99
97
|
|
@@ -161,6 +159,7 @@ module Aspera
|
|
161
159
|
@workspace_name = workspace
|
162
160
|
@cache_user_info = nil
|
163
161
|
@cache_url_token_info = nil
|
162
|
+
@context_cache = nil
|
164
163
|
# init rest params
|
165
164
|
aoc_rest_p = {auth: {type: :oauth2}}
|
166
165
|
# shortcut to auth section
|
@@ -212,7 +211,7 @@ module Aspera
|
|
212
211
|
aoc_auth_p[:aoc_pub_link][:json][:password] = password unless password.nil?
|
213
212
|
# basic auth required for /token
|
214
213
|
aoc_auth_p[:auth] = {type: :basic, username: aoc_auth_p[:client_id], password: aoc_auth_p[:client_secret]}
|
215
|
-
else
|
214
|
+
else error_unexpected_value(aoc_auth_p[:grant_method])
|
216
215
|
end
|
217
216
|
super(aoc_rest_p)
|
218
217
|
end
|
@@ -226,7 +225,7 @@ module Aspera
|
|
226
225
|
end
|
227
226
|
|
228
227
|
def assert_public_link_types(expected)
|
229
|
-
|
228
|
+
assert_values(public_link['purpose'], expected){'public link type'}
|
230
229
|
end
|
231
230
|
|
232
231
|
def additional_persistence_ids
|
@@ -234,12 +233,6 @@ module Aspera
|
|
234
233
|
return [] # TODO : public_link['id'] ?
|
235
234
|
end
|
236
235
|
|
237
|
-
# def secret_finder=(secret_finder)
|
238
|
-
# raise 'secret finder already set' unless @secret_finder.nil?
|
239
|
-
# raise 'secret finder must have lookup_secret' unless secret_finder.respond_to?(:lookup_secret)
|
240
|
-
# @secret_finder = secret_finder
|
241
|
-
# end
|
242
|
-
|
243
236
|
# cached user information
|
244
237
|
def current_user_info(exception: false)
|
245
238
|
return @cache_user_info unless @cache_user_info.nil?
|
@@ -252,7 +245,7 @@ module Aspera
|
|
252
245
|
Log.log.debug{"ignoring error: #{e}"}
|
253
246
|
{}
|
254
247
|
end
|
255
|
-
USER_INFO_FIELDS_MIN.each{|f|@cache_user_info[f] =
|
248
|
+
USER_INFO_FIELDS_MIN.each{|f|@cache_user_info[f] = nil if @cache_user_info[f].nil?}
|
256
249
|
return @cache_user_info
|
257
250
|
end
|
258
251
|
|
@@ -260,7 +253,8 @@ module Aspera
|
|
260
253
|
# @return [Hash] current context information: workspace, and home node/file if app is "Files"
|
261
254
|
def context(application = nil)
|
262
255
|
return @context_cache unless @context_cache.nil?
|
263
|
-
|
256
|
+
assert(!application.nil?){'application must be set once'}
|
257
|
+
assert_values(application, %i[files packages])
|
264
258
|
ws_id =
|
265
259
|
if !public_link.nil?
|
266
260
|
Log.log.debug('Using workspace of public link')
|
@@ -323,7 +317,7 @@ module Aspera
|
|
323
317
|
# @param package_info [Hash] created package information
|
324
318
|
# @returns [Aspera::Node] a node API for access key
|
325
319
|
def node_api_from(node_id:, workspace_id: nil, workspace_name: nil, scope: Aspera::Node::SCOPE_USER, package_info: nil)
|
326
|
-
|
320
|
+
assert_type(node_id, String)
|
327
321
|
node_info = read("nodes/#{node_id}")[:data]
|
328
322
|
if workspace_name.nil? && !workspace_id.nil?
|
329
323
|
workspace_name = read("workspaces/#{workspace_id}")[:data]['name']
|
@@ -370,16 +364,16 @@ module Aspera
|
|
370
364
|
Log.log.debug('no metadata in shared inbox')
|
371
365
|
return
|
372
366
|
end
|
367
|
+
assert(pkg_data.key?('metadata')){"package requires metadata: #{meta_schema}"}
|
373
368
|
pkg_meta = pkg_data['metadata']
|
374
|
-
|
375
|
-
raise 'metadata must be an Array' unless pkg_meta.is_a?(Array)
|
369
|
+
assert_type(pkg_meta, Array){'metadata'}
|
376
370
|
Log.log.debug{Log.dump(:metadata, pkg_meta)}
|
377
371
|
pkg_meta.each do |field|
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
372
|
+
assert_type(field, Hash){'metadata field'}
|
373
|
+
assert(field.key?('name')){'metadata field must have name'}
|
374
|
+
assert(field.key?('values')){'metadata field must have values'}
|
375
|
+
assert_type(field['values'], Array){'metadata field values'}
|
376
|
+
assert(!meta_schema.select{|i|i['name'].eql?(field['name'])}.empty?){"unknown metadata field: #{field['name']}"}
|
383
377
|
end
|
384
378
|
meta_schema.each do |field|
|
385
379
|
provided = pkg_meta.select{|i|i['name'].eql?(field['name'])}
|
@@ -396,15 +390,15 @@ module Aspera
|
|
396
390
|
# @return nil package_data is modified
|
397
391
|
def resolve_package_recipients(package_data, ws_id, recipient_list_field, new_user_option)
|
398
392
|
return unless package_data.key?(recipient_list_field)
|
399
|
-
|
393
|
+
assert_type(package_data[recipient_list_field], Array){recipient_list_field}
|
400
394
|
new_user_option = {'package_contact' => true} if new_user_option.nil?
|
401
|
-
|
395
|
+
assert_type(new_user_option, Hash){'new_user_option'}
|
402
396
|
# list with resolved elements
|
403
397
|
resolved_list = []
|
404
398
|
package_data[recipient_list_field].each do |short_recipient_info|
|
405
399
|
case short_recipient_info
|
406
400
|
when Hash # native API information, check keys
|
407
|
-
|
401
|
+
assert(short_recipient_info.keys.sort.eql?(%w[id type])){"#{recipient_list_field} element shall have fields: id and type"}
|
408
402
|
when String # CLI helper: need to resolve provided name to type/id
|
409
403
|
# email: user, else dropbox
|
410
404
|
entity_type = short_recipient_info.include?('@') ? 'contacts' : 'dropboxes'
|
@@ -450,7 +444,7 @@ module Aspera
|
|
450
444
|
})
|
451
445
|
end
|
452
446
|
pkg_data['metadata'] = api_meta
|
453
|
-
else
|
447
|
+
else error_unexpected_value(pkg_meta.class)
|
454
448
|
end
|
455
449
|
return nil
|
456
450
|
end
|
@@ -513,7 +507,7 @@ module Aspera
|
|
513
507
|
# Console cookie
|
514
508
|
################
|
515
509
|
# we are sure that fields are not nil
|
516
|
-
cookie_elements = [app_info[:app], current_user_info['name'], current_user_info['email']].map{|e|Base64.strict_encode64(e)}
|
510
|
+
cookie_elements = [app_info[:app], current_user_info['name'] || 'public link', current_user_info['email'] || 'none'].map{|e|Base64.strict_encode64(e)}
|
517
511
|
cookie_elements.unshift(COOKIE_PREFIX_CONSOLE_AOC)
|
518
512
|
transfer_spec['cookie'] = cookie_elements.join(':')
|
519
513
|
# Application tags
|
@@ -590,7 +584,8 @@ module Aspera
|
|
590
584
|
# @param app_info [Hash] hash with app info
|
591
585
|
# @param types [Array] event types
|
592
586
|
def permissions_send_event(created_data:, app_info:, types: PERMISSIONS_CREATED)
|
593
|
-
|
587
|
+
assert_type(types, Array)
|
588
|
+
assert(!types.empty?)
|
594
589
|
event_creation = {
|
595
590
|
'types' => types,
|
596
591
|
'node_id' => app_info[:node_info]['id'],
|
data/lib/aspera/ascmd.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
# cspell:ignore ascmd smode errstr zstr zmode zuid zgid zctime zatime zmtime fcount dcount btype blist codeset lc_ctype ascmdtypes
|
4
4
|
require 'aspera/log'
|
5
|
+
require 'aspera/assert'
|
5
6
|
|
6
7
|
module Aspera
|
7
8
|
# Run +ascmd+ commands using specified executor (usually, remotely on transfer node)
|
@@ -52,7 +53,7 @@ module Aspera
|
|
52
53
|
end
|
53
54
|
end
|
54
55
|
# for info, second overrides first, so restore it
|
55
|
-
case result.keys.length; when 0 then result = system_info; when 1 then result = result[result.keys.first]; else
|
56
|
+
case result.keys.length; when 0 then result = system_info; when 1 then result = result[result.keys.first]; else error_unexpected_value(result.keys.length); end
|
56
57
|
# raise error as exception
|
57
58
|
raise Error.new(result[:errno], result[:errstr], action_sym, arguments) if
|
58
59
|
result.is_a?(Hash) && (result.keys.sort == TYPES_DESCR[:error][:fields].map{|i|i[:name]}.sort)
|
@@ -65,10 +66,6 @@ module Aspera
|
|
65
66
|
super(); @errno = errno; @errstr = errstr; @command = cmd; @arguments = arguments; end # rubocop:disable Style/Semicolon
|
66
67
|
|
67
68
|
def message; "ascmd: #{@errstr} (#{@errno})"; end
|
68
|
-
|
69
|
-
# TODO : delete : attr_reader :errno #, :errstr, :command
|
70
|
-
# TODO : delete :def args; @arguments; end
|
71
|
-
|
72
69
|
def extended_message; "ascmd: errno=#{@errno} errstr=\"#{@errstr}\" command=#{@command} arguments=#{@arguments.join(',')}"; end
|
73
70
|
end # Error
|
74
71
|
|
@@ -177,7 +174,7 @@ module Aspera
|
|
177
174
|
end
|
178
175
|
end
|
179
176
|
end
|
180
|
-
else
|
177
|
+
else error_unexpected_value(type_descr[:decode])
|
181
178
|
end # is_a
|
182
179
|
return result
|
183
180
|
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
|
+
end
|
10
|
+
|
11
|
+
class Object
|
12
|
+
def assert(assertion, info = nil, level: 2, exception_class: Aspera::AssertError)
|
13
|
+
raise Aspera::InternalError, 'bad assert: both info and block given' unless info.nil? || !block_given?
|
14
|
+
return if assertion
|
15
|
+
message = 'assertion failed'
|
16
|
+
info = yield if block_given?
|
17
|
+
message = "#{message}: #{info}" if info
|
18
|
+
message = "#{message}: #{caller(level..level).first}"
|
19
|
+
raise exception_class, message
|
20
|
+
end
|
21
|
+
|
22
|
+
# assert that value has the given type
|
23
|
+
# @param value [Object] the value to check
|
24
|
+
# @param type [Class] the expected type
|
25
|
+
def assert_type(value, type, exception_class: Aspera::AssertError)
|
26
|
+
assert(value.is_a?(type), level: 3, exception_class: exception_class){"#{block_given? ? "#{yield}: " : nil}expecting #{type}, but have #{value.inspect}"}
|
27
|
+
end
|
28
|
+
|
29
|
+
# assert that value is one of the given values
|
30
|
+
def assert_values(value, values, exception_class: Aspera::AssertError)
|
31
|
+
assert(values.include?(value), level: 3, exception_class: exception_class) do
|
32
|
+
"#{block_given? ? "#{yield}: " : nil}expecting one of #{values.inspect}, but have #{value.inspect}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# the line with this shall never be reached
|
37
|
+
def error_unreachable_line
|
38
|
+
raise Aspera::InternalError, "unreachable line reached: #{caller(2..2).first}"
|
39
|
+
end
|
40
|
+
|
41
|
+
# the value is not one of the expected values
|
42
|
+
def error_unexpected_value(value, exception_class: Aspera::InternalError)
|
43
|
+
raise exception_class, "#{block_given? ? "#{yield}: " : nil}unexpected value: #{value.inspect}"
|
44
|
+
end
|
45
|
+
end
|
@@ -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
|
+
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
|
+
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)
|
@@ -155,16 +158,26 @@ module Aspera
|
|
155
158
|
end
|
156
159
|
|
157
160
|
def option_handler(option_symbol, operation, value=nil)
|
161
|
+
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 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 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 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
|
+
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
|
+
assert_type(results, Hash)
|
313
|
+
assert((results.keys - RESULT_PARAMS).empty?){"result unsupported key: #{results.keys - RESULT_PARAMS}"}
|
314
|
+
assert(results.key?(:type)){"result must have type (#{results})"}
|
315
|
+
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
|
+
assert_type(obj_list, Array)
|
340
|
+
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
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
require 'aspera/fasp/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
|
|
@@ -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
|
+
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 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
@@ -8,8 +8,10 @@ require 'aspera/cli/transfer_agent'
|
|
8
8
|
require 'aspera/cli/version'
|
9
9
|
require 'aspera/cli/info'
|
10
10
|
require 'aspera/cli/hints'
|
11
|
+
require 'aspera/preview/terminal'
|
11
12
|
require 'aspera/secret_hider'
|
12
13
|
require 'aspera/log'
|
14
|
+
require 'aspera/assert'
|
13
15
|
|
14
16
|
module Aspera
|
15
17
|
module Cli
|
@@ -53,6 +55,14 @@ module Aspera
|
|
53
55
|
raise global_status unless global_status.eql?(:success)
|
54
56
|
return {type: :object_list, data: status_table}
|
55
57
|
end
|
58
|
+
|
59
|
+
def result_picture_in_terminal(options, blob)
|
60
|
+
terminal_options = options.get_option(:query, default: {}).symbolize_keys
|
61
|
+
allowed_options = Preview::Terminal.method(:build).parameters.select{|i|i[0].eql?(:key)}.map{|i|i[1]}
|
62
|
+
unknown_options = terminal_options.keys - allowed_options
|
63
|
+
raise "invalid options: #{unknown_options.join(', ')}, use #{allowed_options.join(', ')}" unless unknown_options.empty?
|
64
|
+
return Main.result_status(Preview::Terminal.build(blob, **terminal_options))
|
65
|
+
end
|
56
66
|
end # self
|
57
67
|
|
58
68
|
private
|
@@ -78,9 +88,10 @@ module Aspera
|
|
78
88
|
|
79
89
|
# This can throw exception if there is a problem with the environment, needs to be caught by execute method
|
80
90
|
def init_agents_and_options
|
81
|
-
#
|
82
|
-
early_debug_setup
|
91
|
+
# create formatter, in case there is an exception, it is used to display.
|
83
92
|
@agents[:formatter] = Formatter.new
|
93
|
+
# second : manage debug level (allows debugging of option parser)
|
94
|
+
early_debug_setup
|
84
95
|
@agents[:options] = Manager.new(PROGRAM_NAME)
|
85
96
|
# give command line arguments to option manager
|
86
97
|
options.parse_command_line(@argv)
|
@@ -96,7 +107,7 @@ module Aspera
|
|
96
107
|
# the Config plugin adds the @preset parser, so declare before TransferAgent which may use it
|
97
108
|
@agents[:config] = Plugins::Config.new(@agents, gem: GEM_NAME, name: PROGRAM_NAME, help: DOC_URL, version: Aspera::Cli::VERSION)
|
98
109
|
# data persistency
|
99
|
-
|
110
|
+
assert(@agents[:persistency]){'missing persistency object'}
|
100
111
|
# the TransferAgent plugin may use the @preset parser
|
101
112
|
@agents[:transfer] = TransferAgent.new(options, config)
|
102
113
|
Log.log.debug('plugin env created'.red)
|
@@ -219,6 +230,8 @@ module Aspera
|
|
219
230
|
when /^--log-level=(.*)/ then Aspera::Log.instance.level = Regexp.last_match(1).to_sym
|
220
231
|
when /^--logger=(.*)/ then Aspera::Log.instance.logger_type = Regexp.last_match(1).to_sym
|
221
232
|
end
|
233
|
+
rescue => e
|
234
|
+
$stderr.puts("Error: #{e}")
|
222
235
|
end
|
223
236
|
end
|
224
237
|
|