aspera-cli 4.13.0 → 4.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +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
|