aspera-cli 4.13.0 → 4.14.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/CHANGELOG.md +28 -5
- data/CONTRIBUTING.md +17 -1
- data/README.md +782 -401
- data/examples/dascli +1 -1
- data/examples/rubyc +24 -0
- data/lib/aspera/aoc.rb +21 -32
- data/lib/aspera/ascmd.rb +1 -0
- data/lib/aspera/cli/basic_auth_plugin.rb +6 -6
- data/lib/aspera/cli/formatter.rb +17 -25
- data/lib/aspera/cli/main.rb +21 -27
- data/lib/aspera/cli/manager.rb +128 -114
- data/lib/aspera/cli/plugin.rb +87 -38
- data/lib/aspera/cli/plugins/alee.rb +2 -2
- data/lib/aspera/cli/plugins/aoc.rb +216 -102
- data/lib/aspera/cli/plugins/ats.rb +16 -18
- data/lib/aspera/cli/plugins/bss.rb +3 -3
- data/lib/aspera/cli/plugins/config.rb +177 -367
- data/lib/aspera/cli/plugins/console.rb +4 -6
- data/lib/aspera/cli/plugins/cos.rb +12 -13
- data/lib/aspera/cli/plugins/faspex.rb +17 -18
- data/lib/aspera/cli/plugins/faspex5.rb +332 -216
- data/lib/aspera/cli/plugins/node.rb +171 -142
- data/lib/aspera/cli/plugins/orchestrator.rb +15 -18
- data/lib/aspera/cli/plugins/preview.rb +38 -60
- data/lib/aspera/cli/plugins/server.rb +22 -15
- data/lib/aspera/cli/plugins/shares.rb +24 -33
- data/lib/aspera/cli/plugins/sync.rb +3 -3
- data/lib/aspera/cli/transfer_agent.rb +29 -26
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/colors.rb +9 -7
- data/lib/aspera/data/6 +0 -0
- data/lib/aspera/environment.rb +7 -3
- data/lib/aspera/fasp/agent_connect.rb +5 -0
- data/lib/aspera/fasp/agent_direct.rb +5 -5
- data/lib/aspera/fasp/agent_httpgw.rb +138 -60
- data/lib/aspera/fasp/agent_trsdk.rb +2 -0
- data/lib/aspera/fasp/error_info.rb +2 -0
- data/lib/aspera/fasp/installation.rb +18 -19
- data/lib/aspera/fasp/parameters.rb +18 -17
- data/lib/aspera/fasp/parameters.yaml +2 -1
- data/lib/aspera/fasp/resume_policy.rb +3 -3
- data/lib/aspera/fasp/transfer_spec.rb +6 -5
- data/lib/aspera/fasp/uri.rb +23 -21
- data/lib/aspera/faspex_postproc.rb +1 -1
- data/lib/aspera/hash_ext.rb +12 -2
- data/lib/aspera/keychain/macos_security.rb +13 -13
- data/lib/aspera/log.rb +1 -0
- data/lib/aspera/node.rb +62 -80
- data/lib/aspera/oauth.rb +1 -1
- data/lib/aspera/persistency_action_once.rb +1 -1
- data/lib/aspera/preview/terminal.rb +61 -15
- data/lib/aspera/preview/utils.rb +3 -3
- data/lib/aspera/proxy_auto_config.js +2 -2
- data/lib/aspera/rest.rb +37 -0
- data/lib/aspera/secret_hider.rb +6 -1
- data/lib/aspera/ssh.rb +1 -1
- data/lib/aspera/sync.rb +2 -0
- data.tar.gz.sig +0 -0
- metadata +3 -4
- metadata.gz.sig +0 -0
- data/docs/test_env.conf +0 -186
- data/lib/aspera/data/7 +0 -0
@@ -54,43 +54,42 @@ module Aspera
|
|
54
54
|
# used to trigger periodic processing
|
55
55
|
@periodic = TimerLimiter.new(LOG_LIMITER_SEC)
|
56
56
|
# link CLI options to gen_info attributes
|
57
|
-
options.
|
58
|
-
|
59
|
-
|
60
|
-
options.
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
options.
|
66
|
-
options.
|
67
|
-
options.
|
68
|
-
options.
|
69
|
-
options.
|
70
|
-
options.
|
71
|
-
options.
|
72
|
-
options.
|
73
|
-
options.
|
74
|
-
options.
|
75
|
-
|
76
|
-
|
77
|
-
|
57
|
+
options.declare(
|
58
|
+
:skip_format, 'Skip this preview format (multiple possible)', values: Aspera::Preview::Generator::PREVIEW_FORMATS,
|
59
|
+
handler: {o: self, m: :option_skip_format}, default: [])
|
60
|
+
options.declare(
|
61
|
+
:folder_reset_cache, 'Force detection of generated preview by refresh cache',
|
62
|
+
values: %i[no header read],
|
63
|
+
handler: {o: self, m: :option_folder_reset_cache},
|
64
|
+
default: :no)
|
65
|
+
options.declare(:skip_types, 'Skip types in comma separated list', handler: {o: self, m: :option_skip_types})
|
66
|
+
options.declare(:previews_folder, 'Preview folder in storage root', handler: {o: self, m: :option_previews_folder}, default: DEFAULT_PREVIEWS_FOLDER)
|
67
|
+
options.declare(:temp_folder, 'Path to temp folder', default: Dir.tmpdir)
|
68
|
+
options.declare(:skip_folders, 'List of folder to skip', handler: {o: self, m: :option_skip_folders}, default: [])
|
69
|
+
options.declare(:case, 'Basename of output for for test')
|
70
|
+
options.declare(:scan_path, 'Subpath in folder id to start scan in (default=/)')
|
71
|
+
options.declare(:scan_id, 'Folder id in storage to start scan in, default is access key main folder id')
|
72
|
+
options.declare(:mimemagic, 'Use Mime type detection of gem mimemagic', values: :bool, default: false)
|
73
|
+
options.declare(:overwrite, 'When to overwrite result file', values: %i[always never mtime], handler: {o: self, m: :option_overwrite}, default: :mtime)
|
74
|
+
options.declare(
|
75
|
+
:file_access, 'How to read and write files in repository',
|
76
|
+
values: %i[local remote],
|
77
|
+
handler: {o: self, m: :option_file_access},
|
78
|
+
default: :local)
|
78
79
|
|
79
80
|
# add other options for generator (and set default values)
|
80
81
|
Aspera::Preview::Options::DESCRIPTIONS.each do |opt|
|
81
|
-
|
82
|
-
|
83
|
-
options.add_opt_list(opt[:name], opt[:values], opt[:description])
|
82
|
+
values = if opt.key?(:values)
|
83
|
+
opt[:values]
|
84
84
|
elsif Cli::Manager::BOOLEAN_SIMPLE.include?(opt[:default])
|
85
|
-
|
86
|
-
else
|
87
|
-
options.add_opt_simple(opt[:name], opt[:description])
|
85
|
+
:bool
|
88
86
|
end
|
87
|
+
options.declare(opt[:name], opt[:description].capitalize, values: values, handler: {o: @gen_options, m: opt[:name]}, default: opt[:default])
|
89
88
|
end
|
90
89
|
|
91
90
|
options.parse_options!
|
92
91
|
raise 'skip_folder shall be an Array, use @json:[...]' unless @option_skip_folders.is_a?(Array)
|
93
|
-
@tmp_folder = File.join(options.get_option(:temp_folder,
|
92
|
+
@tmp_folder = File.join(options.get_option(:temp_folder, mandatory: true), "#{TMP_DIR_PREFIX}.#{SecureRandom.uuid}")
|
94
93
|
FileUtils.mkdir_p(@tmp_folder)
|
95
94
|
Log.log.debug{"tmpdir: #{@tmp_folder}"}
|
96
95
|
end
|
@@ -206,34 +205,12 @@ module Aspera
|
|
206
205
|
end
|
207
206
|
|
208
207
|
def do_transfer(direction, folder_id, source_filename, destination='/')
|
209
|
-
raise '
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
template_ts = res[:data]['transfer_specs'].first['transfer_spec']
|
214
|
-
# get ports, anyway that should be 33001 for both. add remote_user ?
|
215
|
-
@default_transfer_spec = %w[ssh_port fasp_port].each_with_object({}){|e, h|h[e] = template_ts[e]; }
|
216
|
-
if !@default_transfer_spec['remote_user'].eql?(Aspera::Fasp::TransferSpec::ACCESS_KEY_TRANSFER_USER)
|
217
|
-
Log.log.warn('remote_user shall be xfer')
|
218
|
-
@default_transfer_spec['remote_user'] = Aspera::Fasp::TransferSpec::ACCESS_KEY_TRANSFER_USER
|
219
|
-
end
|
220
|
-
@api_node.ts_basic_token(@default_transfer_spec)
|
221
|
-
# NOTE: we use the same address for ascp than for node api instead of the one from upload_setup
|
222
|
-
# TODO: configurable ? useful ?
|
223
|
-
@default_transfer_spec['remote_host'] = @transfer_server_address
|
224
|
-
end
|
225
|
-
t_spec = @default_transfer_spec.merge({
|
226
|
-
'direction' => direction,
|
227
|
-
'paths' => [{'source' => source_filename}],
|
228
|
-
'tags' => {
|
229
|
-
Fasp::TransferSpec::TAG_RESERVED => {
|
230
|
-
PREV_GEN_TAG => true,
|
231
|
-
'node' => {
|
232
|
-
'access_key' => @access_key_self['id'],
|
233
|
-
'file_id' => folder_id }}}
|
208
|
+
raise 'Internal ERROR' if destination.nil? && direction.eql?(Fasp::TransferSpec::DIRECTION_RECEIVE)
|
209
|
+
t_spec = @api_node.transfer_spec_gen4(folder_id, direction, {
|
210
|
+
'paths' => [{'source' => source_filename}],
|
211
|
+
'tags' => {Fasp::TransferSpec::TAG_RESERVED => {PREV_GEN_TAG => true}}
|
234
212
|
})
|
235
|
-
# force destination
|
236
|
-
# t_spec['destination_root']=destination
|
213
|
+
# force destination, need to set this in transfer agent else it gets overwritten, not do: t_spec['destination_root']=destination
|
237
214
|
transfer.option_transfer_spec_deep_merge({'destination_root' => destination})
|
238
215
|
Main.result_transfer(transfer.start(t_spec))
|
239
216
|
end
|
@@ -358,7 +335,7 @@ module Aspera
|
|
358
335
|
scan_start = '/' + scan_start.split('/').reject(&:empty?).join('/')
|
359
336
|
scan_start = "#{scan_start}/" # unless scan_start.end_with?('/')
|
360
337
|
end
|
361
|
-
filter_block = Aspera::Node.file_matcher(
|
338
|
+
filter_block = Aspera::Node.file_matcher(value_or_query(allowed_types: String))
|
362
339
|
Log.log.debug{"scan: #{top_entry} : #{scan_start}".green}
|
363
340
|
# don't use recursive call, use list instead
|
364
341
|
entries_to_process = [top_entry]
|
@@ -455,7 +432,7 @@ module Aspera
|
|
455
432
|
end
|
456
433
|
end
|
457
434
|
end
|
458
|
-
Aspera::Preview::FileTypes.instance.use_mimemagic = options.get_option(:mimemagic,
|
435
|
+
Aspera::Preview::FileTypes.instance.use_mimemagic = options.get_option(:mimemagic, mandatory: true)
|
459
436
|
# check tools that are anyway required for all cases
|
460
437
|
Aspera::Preview::Utils.check_tools(@skip_types)
|
461
438
|
case command
|
@@ -477,15 +454,15 @@ module Aspera
|
|
477
454
|
return Main.result_status('scan finished')
|
478
455
|
when :events, :trevents
|
479
456
|
iteration_persistency = nil
|
480
|
-
if options.get_option(:once_only,
|
457
|
+
if options.get_option(:once_only, mandatory: true)
|
481
458
|
iteration_persistency = PersistencyActionOnce.new(
|
482
459
|
manager: @agents[:persistency],
|
483
460
|
data: [],
|
484
461
|
id: IdGenerator.from_list([
|
485
462
|
'preview_iteration',
|
486
463
|
command.to_s,
|
487
|
-
options.get_option(:url,
|
488
|
-
options.get_option(:username,
|
464
|
+
options.get_option(:url, mandatory: true),
|
465
|
+
options.get_option(:username, mandatory: true)
|
489
466
|
]))
|
490
467
|
end
|
491
468
|
# call processing method specified by command line command
|
@@ -506,6 +483,7 @@ module Aspera
|
|
506
483
|
raise 'error'
|
507
484
|
end
|
508
485
|
ensure
|
486
|
+
Log.log.debug{"cleaning up temp folder #{@tmp_folder}"}
|
509
487
|
FileUtils.rm_rf(@tmp_folder)
|
510
488
|
end # execute_action
|
511
489
|
end # Preview
|
@@ -25,7 +25,9 @@ module Aspera
|
|
25
25
|
|
26
26
|
class Server < Aspera::Cli::BasicAuthPlugin
|
27
27
|
SSH_SCHEME = 'ssh'
|
28
|
-
|
28
|
+
LOCAL_SCHEME = 'local'
|
29
|
+
HTTPS_SCHEME = 'https'
|
30
|
+
URI_SCHEMES = [SSH_SCHEME, LOCAL_SCHEME, HTTPS_SCHEME].freeze
|
29
31
|
ASCMD_ALIASES = {
|
30
32
|
browse: :ls,
|
31
33
|
delete: :rm,
|
@@ -41,7 +43,7 @@ module Aspera
|
|
41
43
|
cmd = cmd.map{|v|%Q("#{v}")}.join(' ') if cmd.is_a?(Array)
|
42
44
|
Log.log.debug{"Executing: #{cmd} with '#{line}'"}
|
43
45
|
stdout_str, stderr_str, status = Open3.capture3(cmd, stdin_data: line, binmode: true)
|
44
|
-
Log.log.debug
|
46
|
+
Log.log.debug{"exec status: #{status} -> #{stderr_str}"}
|
45
47
|
raise "command #{cmd} failed with code #{status.exitstatus} #{stderr_str}" unless status.success?
|
46
48
|
return stdout_str
|
47
49
|
end
|
@@ -49,28 +51,32 @@ module Aspera
|
|
49
51
|
|
50
52
|
def initialize(env)
|
51
53
|
super(env)
|
52
|
-
options.
|
53
|
-
options.
|
54
|
+
options.declare(:ssh_keys, 'SSH key path list (Array or single)')
|
55
|
+
options.declare(:passphrase, 'SSH private key passphrase')
|
56
|
+
options.declare(:ssh_options, 'SSH options', types: Hash, default: {})
|
54
57
|
options.parse_options!
|
55
|
-
@ssh_opts =
|
58
|
+
@ssh_opts = options.get_option(:ssh_options).symbolize_keys
|
56
59
|
end
|
57
60
|
|
58
61
|
# Read command line options
|
59
62
|
# @return [Hash] transfer specification
|
60
63
|
def options_to_base_transfer_spec
|
61
|
-
url = options.get_option(:url,
|
64
|
+
url = options.get_option(:url, mandatory: true)
|
62
65
|
server_transfer_spec = {}
|
63
66
|
server_uri = URI.parse(url)
|
64
|
-
Log.log.debug{"URI
|
67
|
+
Log.log.debug{"URI=#{server_uri}, host=#{server_uri.hostname}, port=#{server_uri.port}, scheme=#{server_uri.scheme}"}
|
65
68
|
server_transfer_spec['remote_host'] = server_uri.hostname
|
66
69
|
unless URI_SCHEMES.include?(server_uri.scheme)
|
67
70
|
Log.log.warn{"Scheme [#{server_uri.scheme}] not supported in #{url}, use one of: #{URI_SCHEMES.join(', ')}. Defaulting to #{SSH_SCHEME}."}
|
68
71
|
server_uri.scheme = SSH_SCHEME
|
69
72
|
end
|
70
|
-
if server_uri.scheme.eql?(
|
73
|
+
if server_uri.scheme.eql?(LOCAL_SCHEME)
|
71
74
|
# Using local execution (mostly for testing)
|
75
|
+
server_transfer_spec['remote_host'] = 'localhost'
|
76
|
+
# simulate SSH environment, else ascp will fail
|
77
|
+
ENV['SSH_CLIENT'] = 'local 0 0'
|
72
78
|
return server_transfer_spec
|
73
|
-
elsif transfer.option_transfer_spec['token'].is_a?(String) && server_uri.scheme.eql?(
|
79
|
+
elsif transfer.option_transfer_spec['token'].is_a?(String) && server_uri.scheme.eql?(HTTPS_SCHEME)
|
74
80
|
server_transfer_spec['wss_enabled'] = true
|
75
81
|
server_transfer_spec['wss_port'] = server_uri.port
|
76
82
|
# Using WSS
|
@@ -88,11 +94,7 @@ module Aspera
|
|
88
94
|
options.set_option(:username, Aspera::Fasp::TransferSpec::ACCESS_KEY_TRANSFER_USER)
|
89
95
|
Log.log.info{"No username provided: Assuming default transfer user: #{Aspera::Fasp::TransferSpec::ACCESS_KEY_TRANSFER_USER}"}
|
90
96
|
end
|
91
|
-
server_transfer_spec['remote_user'] = options.get_option(:username,
|
92
|
-
ssh_args = options.get_option(:ssh_options)
|
93
|
-
ssh_args = {} if ssh_args.nil?
|
94
|
-
raise 'expecting a Hash for ssh_options' unless ssh_args.is_a?(Hash)
|
95
|
-
@ssh_opts = ssh_args.symbolize_keys
|
97
|
+
server_transfer_spec['remote_user'] = options.get_option(:username, mandatory: true)
|
96
98
|
if !server_uri.port.nil?
|
97
99
|
@ssh_opts[:port] = server_uri.port
|
98
100
|
server_transfer_spec['ssh_port'] = server_uri.port
|
@@ -119,6 +121,11 @@ module Aspera
|
|
119
121
|
cred_set = true
|
120
122
|
end
|
121
123
|
end
|
124
|
+
ssh_passphrase = options.get_option(:passphrase)
|
125
|
+
if !ssh_passphrase.nil?
|
126
|
+
@ssh_opts[:passphrase] = ssh_passphrase
|
127
|
+
server_transfer_spec['ssh_private_key_passphrase'] = ssh_passphrase
|
128
|
+
end
|
122
129
|
# if user provided transfer spec has a token, we will use bypass keys
|
123
130
|
cred_set = true if transfer.option_transfer_spec['token'].is_a?(String)
|
124
131
|
raise 'Either password, key , or transfer spec token must be provided' if !cred_set
|
@@ -143,7 +150,7 @@ module Aspera
|
|
143
150
|
|
144
151
|
def execute_action
|
145
152
|
server_transfer_spec = options_to_base_transfer_spec
|
146
|
-
ascmd_executor = if !@ssh_opts.
|
153
|
+
ascmd_executor = if !@ssh_opts.empty?
|
147
154
|
Ssh.new(server_transfer_spec['remote_host'], server_transfer_spec['remote_user'], @ssh_opts)
|
148
155
|
elsif server_transfer_spec.key?('wss_enabled')
|
149
156
|
nil
|
@@ -26,24 +26,25 @@ module Aspera
|
|
26
26
|
|
27
27
|
def initialize(env)
|
28
28
|
super(env)
|
29
|
-
options.
|
30
|
-
options.set_option(:type, :any)
|
29
|
+
options.declare(:type, 'Type of user/group for operations', values: %i[any local ldap saml], default: :any)
|
31
30
|
options.parse_options!
|
32
31
|
end
|
33
32
|
|
34
33
|
SAML_IMPORT_MANDATORY = %w[id name_id].freeze
|
35
34
|
SAML_IMPORT_ALLOWED = %w[email given_name surname].concat(SAML_IMPORT_MANDATORY).freeze
|
36
35
|
|
37
|
-
ACTIONS = %i[health
|
36
|
+
ACTIONS = %i[health files admin].freeze
|
37
|
+
# common to users and groups
|
38
|
+
USR_GRP_SETTINGS = %i[transfer_settings app_authorizations share_permissions].freeze
|
38
39
|
|
39
40
|
def execute_action
|
40
|
-
command = options.get_next_command(ACTIONS)
|
41
|
+
command = options.get_next_command(ACTIONS, aliases: {repository: :files})
|
41
42
|
case command
|
42
43
|
when :health
|
43
44
|
nagios = Nagios.new
|
44
45
|
begin
|
45
46
|
Rest
|
46
|
-
.new(base_url: options.get_option(:url,
|
47
|
+
.new(base_url: options.get_option(:url, mandatory: true) + '/node_api')
|
47
48
|
.call(
|
48
49
|
operation: 'GET',
|
49
50
|
subpath: 'ping',
|
@@ -54,25 +55,28 @@ module Aspera
|
|
54
55
|
nagios.add_critical('node api', e.to_s)
|
55
56
|
end
|
56
57
|
return nagios.result
|
57
|
-
when :repository
|
58
|
+
when :repository, :files
|
58
59
|
api_shares_node = basic_auth_api('node_api')
|
59
60
|
repo_command = options.get_next_command(Node::COMMANDS_SHARES)
|
60
61
|
return Node.new(@agents.merge(skip_basic_auth_options: true, node_api: api_shares_node)).execute_action(repo_command)
|
61
62
|
when :admin
|
62
63
|
api_shares_admin = basic_auth_api('api/v1')
|
63
|
-
admin_command = options.get_next_command(%i[user group share node])
|
64
|
+
admin_command = options.get_next_command(%i[user group share node].freeze)
|
64
65
|
case admin_command
|
65
66
|
when :node
|
66
67
|
return entity_action(api_shares_admin, 'data/nodes')
|
67
68
|
when :user, :group
|
68
69
|
entity_type = admin_command
|
69
|
-
entities_location = options.get_option(:type,
|
70
|
+
entities_location = options.get_option(:type, mandatory: true)
|
70
71
|
entities_path = "data/#{entities_location}_#{entity_type}s"
|
71
72
|
entity_action = nil
|
72
73
|
case entities_location
|
73
74
|
when :any
|
74
75
|
entities_path = "data/#{entity_type}s"
|
75
|
-
entity_action = %i[list show delete
|
76
|
+
entity_action = %i[list show delete]
|
77
|
+
entity_action.concat(USR_GRP_SETTINGS)
|
78
|
+
entity_action.push(:users) if entity_type.eql?(:group)
|
79
|
+
entity_action.freeze
|
76
80
|
when :local
|
77
81
|
entity_action = %i[list show create modify delete].freeze
|
78
82
|
when :ldap
|
@@ -80,31 +84,15 @@ module Aspera
|
|
80
84
|
when :saml
|
81
85
|
entity_action = %i[import].freeze
|
82
86
|
end
|
83
|
-
|
84
|
-
entity_path = "#{entities_path}/#{instance_identifier}" if %i[app_authorizations share_permissions].include?(
|
85
|
-
case
|
86
|
-
when
|
87
|
+
entity_verb = options.get_next_command(entity_action)
|
88
|
+
# entity_path = "#{entities_path}/#{instance_identifier}" if %i[app_authorizations share_permissions].include?(entity_verb)
|
89
|
+
case entity_verb
|
90
|
+
when *Plugin::ALL_OPS
|
87
91
|
display_fields = entity_type.eql?(:user) ? %w[id username first_name last_name email] : nil
|
88
92
|
display_fields.push(:directory_user) if entity_type.eql?(:user) && entities_location.eql?(:any)
|
89
|
-
return entity_command(
|
90
|
-
when :app_authorizations
|
91
|
-
case options.get_next_command(%i[modify show])
|
92
|
-
when :show
|
93
|
-
return {type: :single_object, data: api_shares_admin.read("#{entity_path}/app_authorizations")[:data]}
|
94
|
-
when :modify
|
95
|
-
parameters = options.get_option(:value, is_type: :mandatory)
|
96
|
-
return {type: :single_object, data: api_shares_admin.update("#{entity_path}/app_authorizations", parameters)[:data]}
|
97
|
-
end
|
98
|
-
when :share_permissions
|
99
|
-
case options.get_next_command(%i[list show])
|
100
|
-
when :list
|
101
|
-
return {type: :object_list, data: api_shares_admin.read("#{entity_path}/share_permissions")[:data]}
|
102
|
-
when :show
|
103
|
-
return {type: :single_object, data: api_shares_admin.read("#{entity_path}/share_permissions/#{instance_identifier}")[:data]}
|
104
|
-
end
|
93
|
+
return entity_command(entity_verb, api_shares_admin, entities_path, display_fields: display_fields)
|
105
94
|
when :import
|
106
|
-
|
107
|
-
return do_bulk_operation(parameters, 'created') do |entity_parameters|
|
95
|
+
return do_bulk_operation(value_create_modify(type: :bulk_hash), 'created') do |entity_parameters|
|
108
96
|
entity_parameters = entity_parameters.transform_keys{|k|k.gsub(/\s+/, '_').downcase}
|
109
97
|
raise 'expecting Hash' unless entity_parameters.is_a?(Hash)
|
110
98
|
SAML_IMPORT_MANDATORY.each{|p|raise "missing mandatory field: #{p}" if entity_parameters[p].nil?}
|
@@ -114,11 +102,14 @@ module Aspera
|
|
114
102
|
api_shares_admin.create("#{entities_path}/import", entity_parameters)[:data]
|
115
103
|
end
|
116
104
|
when :add
|
117
|
-
|
118
|
-
return do_bulk_operation(parameters, 'created') do |entity_name|
|
105
|
+
return do_bulk_operation(value_create_modify(type: :bulk_hash), 'created') do |entity_name|
|
119
106
|
raise "expecting string (name), have #{entity_name.class}" unless entity_name.is_a?(String)
|
120
107
|
api_shares_admin.create(entities_path, {entity_type=>entity_name})[:data]
|
121
108
|
end
|
109
|
+
when *USR_GRP_SETTINGS
|
110
|
+
group_id = instance_identifier
|
111
|
+
entities_path = "#{entities_path}/#{group_id}/#{entity_verb}"
|
112
|
+
return entity_action(api_shares_admin, entities_path, is_singleton: !entity_verb.eql?(:share_permissions))
|
122
113
|
end
|
123
114
|
when :share
|
124
115
|
share_command = options.get_next_command(%i[user_permissions group_permissions].concat(Plugin::ALL_OPS))
|
@@ -13,11 +13,11 @@ module Aspera
|
|
13
13
|
class Sync < Aspera::Cli::Plugin
|
14
14
|
def initialize(env, sync_spec: nil)
|
15
15
|
super(env)
|
16
|
-
options.
|
17
|
-
options.
|
16
|
+
options.declare(:sync_info, 'Information for sync instance and sessions', types: Hash)
|
17
|
+
options.declare(:sync_session, 'Name of session to use for admin commands. default: first in parameters')
|
18
18
|
options.parse_options!
|
19
19
|
return if env[:man_only]
|
20
|
-
@params = options.get_option(:sync_info,
|
20
|
+
@params = options.get_option(:sync_info, mandatory: true)
|
21
21
|
@sync_spec = sync_spec
|
22
22
|
end
|
23
23
|
|
@@ -54,18 +54,13 @@ module Aspera
|
|
54
54
|
@progress_listener = Listener::ProgressMulti.new
|
55
55
|
# source/destination pair, like "paths" of transfer spec
|
56
56
|
@transfer_paths = nil
|
57
|
-
@opt_mgr.
|
58
|
-
@opt_mgr.
|
59
|
-
@opt_mgr.
|
60
|
-
@opt_mgr.
|
61
|
-
@opt_mgr.
|
62
|
-
@opt_mgr.
|
63
|
-
@opt_mgr.
|
64
|
-
@opt_mgr.add_opt_simple(:transfer_info, 'Parameters for transfer agent (Hash)')
|
65
|
-
@opt_mgr.add_opt_list(:progress, %i[none native multi], 'Type of progress bar')
|
66
|
-
@opt_mgr.set_option(:transfer, :direct)
|
67
|
-
@opt_mgr.set_option(:src_type, :list)
|
68
|
-
@opt_mgr.set_option(:progress, :native) # use native ascp progress bar as it is more reliable
|
57
|
+
@opt_mgr.declare(:ts, 'Override transfer spec values', types: Hash, handler: {o: self, m: :option_transfer_spec})
|
58
|
+
@opt_mgr.declare(:to_folder, 'Destination folder for transferred files')
|
59
|
+
@opt_mgr.declare(:sources, "How list of transferred files is provided (#{FILE_LIST_OPTIONS.join(',')})")
|
60
|
+
@opt_mgr.declare(:src_type, 'Type of file list', values: %i[list pair], default: :list)
|
61
|
+
@opt_mgr.declare(:transfer, 'Type of transfer agent', values: TRANSFER_AGENTS, default: :direct)
|
62
|
+
@opt_mgr.declare(:transfer_info, 'Parameters for transfer agent', types: Hash, handler: {o: self, m: :option_transfer_info})
|
63
|
+
@opt_mgr.declare(:progress, 'Type of progress bar', values: %i[none native multi], default: :native)
|
69
64
|
@opt_mgr.parse_options!
|
70
65
|
end
|
71
66
|
|
@@ -74,7 +69,18 @@ module Aspera
|
|
74
69
|
# multiple option are merged
|
75
70
|
def option_transfer_spec=(value)
|
76
71
|
raise 'option ts shall be a Hash' unless value.is_a?(Hash)
|
77
|
-
@transfer_spec_cmdline.
|
72
|
+
@transfer_spec_cmdline.deep_merge!(value)
|
73
|
+
end
|
74
|
+
|
75
|
+
# add other transfer spec parameters
|
76
|
+
def option_transfer_spec_deep_merge(ts); @transfer_spec_cmdline.deep_merge!(ts); end
|
77
|
+
|
78
|
+
# @return [Hash] transfer spec with updated values from command line, including removed values
|
79
|
+
def updated_ts(transfer_spec={})
|
80
|
+
transfer_spec.deep_merge!(@transfer_spec_cmdline)
|
81
|
+
# recursively remove values that are nil (user wants to delete)
|
82
|
+
transfer_spec.deep_do { |hash, key, value, _unused| hash.delete(key) if value.nil?}
|
83
|
+
return transfer_spec
|
78
84
|
end
|
79
85
|
|
80
86
|
def option_transfer_info; @transfer_info; end
|
@@ -82,17 +88,15 @@ module Aspera
|
|
82
88
|
# multiple option are merged
|
83
89
|
def option_transfer_info=(value)
|
84
90
|
raise 'option transfer_info shall be a Hash' unless value.is_a?(Hash)
|
85
|
-
@transfer_info.
|
91
|
+
@transfer_info.deep_merge!(value)
|
86
92
|
end
|
87
93
|
|
88
|
-
def option_transfer_spec_deep_merge(ts); @transfer_spec_cmdline.deep_merge!(ts); end
|
89
|
-
|
90
94
|
def agent_instance=(instance)
|
91
95
|
@agent = instance
|
92
96
|
@agent.add_listener(Listener::Logger.new)
|
93
97
|
# use local progress bar if asked so, or if native and non local ascp (because only local ascp has native progress bar)
|
94
|
-
if @opt_mgr.get_option(:progress,
|
95
|
-
(@opt_mgr.get_option(:progress,
|
98
|
+
if @opt_mgr.get_option(:progress, mandatory: true).eql?(:multi) ||
|
99
|
+
(@opt_mgr.get_option(:progress, mandatory: true).eql?(:native) && !instance.class.to_s.eql?('Aspera::Fasp::AgentDirect'))
|
96
100
|
@agent.add_listener(@progress_listener)
|
97
101
|
end
|
98
102
|
end
|
@@ -100,7 +104,7 @@ module Aspera
|
|
100
104
|
# analyze options and create new agent if not already created or set
|
101
105
|
def set_agent_by_options
|
102
106
|
return nil unless @agent.nil?
|
103
|
-
agent_type = @opt_mgr.get_option(:transfer,
|
107
|
+
agent_type = @opt_mgr.get_option(:transfer, mandatory: true)
|
104
108
|
# agent plugin is loaded on demand to avoid loading unnecessary dependencies
|
105
109
|
require "aspera/fasp/agent_#{agent_type}"
|
106
110
|
agent_options = @opt_mgr.get_option(:transfer_info)
|
@@ -113,7 +117,7 @@ module Aspera
|
|
113
117
|
agent_options = @config.preset_by_name(param_set_name)
|
114
118
|
end
|
115
119
|
# special case: native progress bar
|
116
|
-
if agent_type.eql?(:direct) && @opt_mgr.get_option(:progress,
|
120
|
+
if agent_type.eql?(:direct) && @opt_mgr.get_option(:progress, mandatory: true).eql?(:native)
|
117
121
|
agent_options[:quiet] = false
|
118
122
|
end
|
119
123
|
# normalize after getting from user or default node
|
@@ -170,7 +174,7 @@ module Aspera
|
|
170
174
|
when FILE_LIST_FROM_TRANSFER_SPEC
|
171
175
|
Log.log.debug('assume list provided in transfer spec')
|
172
176
|
special_case_direct_with_list =
|
173
|
-
@opt_mgr.get_option(:transfer,
|
177
|
+
@opt_mgr.get_option(:transfer, mandatory: true).eql?(:direct) &&
|
174
178
|
Fasp::Parameters.ts_has_ascp_file_list(@transfer_spec_cmdline, @opt_mgr.get_option(:transfer_info))
|
175
179
|
raise CliBadArgument, 'transfer spec on command line must have sources' if @transfer_paths.nil? && !special_case_direct_with_list
|
176
180
|
# here we assume check of sources is made in transfer agent
|
@@ -185,7 +189,7 @@ module Aspera
|
|
185
189
|
if !@transfer_paths.nil?
|
186
190
|
Log.log.warn('--sources overrides paths from --ts')
|
187
191
|
end
|
188
|
-
case @opt_mgr.get_option(:src_type,
|
192
|
+
case @opt_mgr.get_option(:src_type, mandatory: true)
|
189
193
|
when :list
|
190
194
|
# when providing a list, just specify source
|
191
195
|
@transfer_paths = file_list.map{|i|{'source' => i}}
|
@@ -225,14 +229,13 @@ module Aspera
|
|
225
229
|
end
|
226
230
|
# update command line paths, unless destination already has one
|
227
231
|
@transfer_spec_cmdline['paths'] = transfer_spec['paths'] || ts_source_paths
|
228
|
-
|
229
|
-
|
230
|
-
transfer_spec.delete_if { |_key, value| value.nil? }
|
232
|
+
# updated transfer spec with command line
|
233
|
+
updated_ts(transfer_spec)
|
231
234
|
# create transfer agent
|
232
235
|
set_agent_by_options
|
233
236
|
Log.log.debug{"transfer agent is a #{@agent.class}"}
|
234
237
|
@agent.start_transfer(transfer_spec, token_regenerator: rest_token)
|
235
|
-
# list of
|
238
|
+
# list of: :success or "error message string"
|
236
239
|
result = @agent.wait_for_transfers_completion
|
237
240
|
@progress_listener.reset
|
238
241
|
Fasp::AgentBase.validate_status_list(result)
|
data/lib/aspera/cli/version.rb
CHANGED
data/lib/aspera/colors.rb
CHANGED
@@ -1,16 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# cspell:words
|
4
|
+
|
3
5
|
# simple vt100 colors
|
4
6
|
class String
|
5
7
|
class << self
|
6
8
|
private
|
7
9
|
|
8
|
-
def
|
10
|
+
def vt_cmd(code); "\e[#{code}m"; end
|
9
11
|
end
|
10
12
|
# see https://en.wikipedia.org/wiki/ANSI_escape_code
|
11
13
|
# symbol is the method name added to String
|
12
14
|
# it adds control chars to set color (and reset at the end).
|
13
|
-
|
15
|
+
VT_STYLES = {
|
14
16
|
bold: 1,
|
15
17
|
italic: 3,
|
16
18
|
underline: 4,
|
@@ -33,17 +35,17 @@ class String
|
|
33
35
|
bg_cyan: 46,
|
34
36
|
bg_gray: 47
|
35
37
|
}.freeze
|
36
|
-
private_constant :
|
37
|
-
# defines methods to String, one per entry in
|
38
|
-
|
38
|
+
private_constant :VT_STYLES
|
39
|
+
# defines methods to String, one per entry in VT_STYLES
|
40
|
+
VT_STYLES.each do |name, code|
|
39
41
|
if $stderr.tty?
|
40
|
-
begin_seq =
|
42
|
+
begin_seq = vt_cmd(code)
|
41
43
|
end_code = 0 # by default reset all
|
42
44
|
if code <= 7 then code + 20
|
43
45
|
elsif code <= 37 then 39
|
44
46
|
elsif code <= 47 then 49
|
45
47
|
end
|
46
|
-
end_seq =
|
48
|
+
end_seq = vt_cmd(end_code)
|
47
49
|
define_method(name){"#{begin_seq}#{self}#{end_seq}"}
|
48
50
|
else
|
49
51
|
define_method(name){self}
|
data/lib/aspera/data/6
CHANGED
Binary file
|
data/lib/aspera/environment.rb
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
require 'aspera/log'
|
4
4
|
require 'rbconfig'
|
5
5
|
|
6
|
+
# cspell:words MEBI mswin bccwin
|
7
|
+
|
6
8
|
module Aspera
|
7
9
|
# detect OS, architecture, and specific stuff
|
8
10
|
class Environment
|
@@ -84,12 +86,14 @@ module Aspera
|
|
84
86
|
end
|
85
87
|
|
86
88
|
# value is provided in block
|
87
|
-
def write_file_restricted(path, force: false)
|
89
|
+
def write_file_restricted(path, force: false, mode: nil)
|
88
90
|
raise 'coding error, missing content block' unless block_given?
|
89
91
|
if force || !File.exist?(path)
|
90
|
-
|
92
|
+
# Windows may give error
|
93
|
+
File.unlink(path) rescue nil
|
94
|
+
# content provided by block
|
91
95
|
File.write(path, yield)
|
92
|
-
restrict_file_access(path)
|
96
|
+
restrict_file_access(path, mode: mode)
|
93
97
|
end
|
94
98
|
return path
|
95
99
|
end
|
@@ -9,7 +9,9 @@ require 'tty-spinner'
|
|
9
9
|
module Aspera
|
10
10
|
module Fasp
|
11
11
|
class AgentConnect < Aspera::Fasp::AgentBase
|
12
|
+
# try twice the main init url in sequence
|
12
13
|
CONNECT_START_URIS = ['fasp://initialize', 'fasp://initialize', 'aspera-drive://initialize', 'https://test-connect.ibmaspera.com/']
|
14
|
+
# delay between each try to start connect
|
13
15
|
SLEEP_SEC_BETWEEN_RETRY = 3
|
14
16
|
private_constant :CONNECT_START_URIS, :SLEEP_SEC_BETWEEN_RETRY
|
15
17
|
def initialize(_options)
|
@@ -106,6 +108,9 @@ module Aspera
|
|
106
108
|
when 'failed'
|
107
109
|
spinner&.error
|
108
110
|
raise Fasp::Error, transfer['error_desc']
|
111
|
+
when 'cancelled'
|
112
|
+
spinner&.error
|
113
|
+
raise Fasp::Error, 'Transfer cancelled by user'
|
109
114
|
else
|
110
115
|
raise Fasp::Error, "unknown status: #{transfer['status']}: #{transfer['error_desc']}"
|
111
116
|
end
|