aspera-cli 4.12.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.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +45 -5
  4. data/CONTRIBUTING.md +113 -22
  5. data/README.md +1289 -754
  6. data/bin/ascli +3 -3
  7. data/examples/dascli +1 -1
  8. data/examples/rubyc +24 -0
  9. data/lib/aspera/aoc.rb +63 -74
  10. data/lib/aspera/ascmd.rb +5 -3
  11. data/lib/aspera/cli/basic_auth_plugin.rb +6 -6
  12. data/lib/aspera/cli/extended_value.rb +24 -37
  13. data/lib/aspera/cli/formatter.rb +23 -25
  14. data/lib/aspera/cli/info.rb +2 -4
  15. data/lib/aspera/cli/main.rb +27 -27
  16. data/lib/aspera/cli/manager.rb +143 -120
  17. data/lib/aspera/cli/plugin.rb +88 -43
  18. data/lib/aspera/cli/plugins/alee.rb +2 -2
  19. data/lib/aspera/cli/plugins/aoc.rb +235 -104
  20. data/lib/aspera/cli/plugins/ats.rb +16 -18
  21. data/lib/aspera/cli/plugins/bss.rb +3 -3
  22. data/lib/aspera/cli/plugins/config.rb +190 -373
  23. data/lib/aspera/cli/plugins/console.rb +4 -6
  24. data/lib/aspera/cli/plugins/cos.rb +12 -13
  25. data/lib/aspera/cli/plugins/faspex.rb +21 -21
  26. data/lib/aspera/cli/plugins/faspex5.rb +399 -150
  27. data/lib/aspera/cli/plugins/node.rb +260 -174
  28. data/lib/aspera/cli/plugins/orchestrator.rb +15 -18
  29. data/lib/aspera/cli/plugins/preview.rb +40 -62
  30. data/lib/aspera/cli/plugins/server.rb +33 -16
  31. data/lib/aspera/cli/plugins/shares.rb +24 -33
  32. data/lib/aspera/cli/plugins/sync.rb +6 -6
  33. data/lib/aspera/cli/transfer_agent.rb +47 -30
  34. data/lib/aspera/cli/version.rb +2 -1
  35. data/lib/aspera/colors.rb +9 -7
  36. data/lib/aspera/command_line_builder.rb +2 -1
  37. data/lib/aspera/cos_node.rb +1 -1
  38. data/lib/aspera/data/6 +0 -0
  39. data/lib/aspera/environment.rb +7 -3
  40. data/lib/aspera/fasp/agent_connect.rb +6 -1
  41. data/lib/aspera/fasp/agent_direct.rb +17 -17
  42. data/lib/aspera/fasp/agent_httpgw.rb +138 -60
  43. data/lib/aspera/fasp/agent_node.rb +14 -4
  44. data/lib/aspera/fasp/agent_trsdk.rb +2 -0
  45. data/lib/aspera/fasp/error_info.rb +2 -0
  46. data/lib/aspera/fasp/installation.rb +19 -19
  47. data/lib/aspera/fasp/parameters.rb +29 -20
  48. data/lib/aspera/fasp/parameters.yaml +5 -2
  49. data/lib/aspera/fasp/resume_policy.rb +3 -3
  50. data/lib/aspera/fasp/transfer_spec.rb +8 -5
  51. data/lib/aspera/fasp/uri.rb +23 -21
  52. data/lib/aspera/faspex_gw.rb +1 -0
  53. data/lib/aspera/faspex_postproc.rb +3 -3
  54. data/lib/aspera/hash_ext.rb +12 -2
  55. data/lib/aspera/keychain/macos_security.rb +13 -13
  56. data/lib/aspera/log.rb +1 -0
  57. data/lib/aspera/node.rb +73 -84
  58. data/lib/aspera/oauth.rb +4 -3
  59. data/lib/aspera/persistency_action_once.rb +1 -1
  60. data/lib/aspera/preview/file_types.rb +8 -6
  61. data/lib/aspera/preview/generator.rb +23 -11
  62. data/lib/aspera/preview/options.rb +3 -2
  63. data/lib/aspera/preview/terminal.rb +80 -0
  64. data/lib/aspera/preview/utils.rb +11 -11
  65. data/lib/aspera/proxy_auto_config.js +2 -2
  66. data/lib/aspera/rest.rb +42 -4
  67. data/lib/aspera/rest_call_error.rb +3 -1
  68. data/lib/aspera/secret_hider.rb +10 -5
  69. data/lib/aspera/ssh.rb +1 -1
  70. data/lib/aspera/sync.rb +41 -33
  71. data/lib/aspera/web_server_simple.rb +22 -18
  72. data.tar.gz.sig +0 -0
  73. metadata +40 -48
  74. metadata.gz.sig +0 -0
  75. data/docs/test_env.conf +0 -179
  76. data/examples/aoc.rb +0 -30
  77. data/examples/faspex4.rb +0 -94
  78. data/examples/node.rb +0 -96
  79. data/examples/server.rb +0 -93
  80. data/lib/aspera/data/7 +0 -0
@@ -9,15 +9,11 @@ module Aspera
9
9
  class Orchestrator < Aspera::Cli::BasicAuthPlugin
10
10
  def initialize(env)
11
11
  super(env)
12
- options.add_opt_simple(:params, 'parameters hash table, use @json:{"param":"value"}')
13
- options.add_opt_simple(:result, "specify result value as: 'work step:parameter'")
14
- options.add_opt_boolean(:synchronous, 'work step:parameter expected as result')
15
- options.add_opt_list(:ret_style, %i[header arg ext], 'how return type is requested in api')
16
- options.add_opt_list(:auth_style, %i[arg_pass head_basic apikey], 'authentication type')
17
- options.set_option(:params, {})
18
- options.set_option(:synchronous, :no)
19
- options.set_option(:ret_style, :arg)
20
- options.set_option(:auth_style, :head_basic)
12
+ options.declare(:params, 'Start parameters', types: Hash, default: {})
13
+ options.declare(:result, "Specify result value as: 'work step:parameter'")
14
+ options.declare(:synchronous, 'Work step:parameter expected as result', values: :bool, default: :no)
15
+ options.declare(:ret_style, 'How return type is requested in api', values: %i[header arg ext], default: :arg)
16
+ options.declare(:auth_style, 'Authentication type', values: %i[arg_pass head_basic apikey], default: :head_basic)
21
17
  options.parse_options!
22
18
  end
23
19
 
@@ -49,7 +45,7 @@ module Aspera
49
45
  call_args[:subpath] = "#{opt[:prefix]}/#{call_args[:subpath]}" unless opt[:prefix].nil?
50
46
  # specify id if necessary
51
47
  call_args[:subpath] = "#{call_args[:subpath]}/#{opt[:id]}" if opt.key?(:id)
52
- call_type = options.get_option(:ret_style, is_type: :mandatory)
48
+ call_type = options.get_option(:ret_style, mandatory: true)
53
49
  call_type = opt[:ret_style] if opt.key?(:ret_style)
54
50
  format = 'json'
55
51
  format = opt[:format] if opt.key?(:format)
@@ -73,19 +69,19 @@ module Aspera
73
69
  end
74
70
 
75
71
  def execute_action
76
- rest_params = {base_url: options.get_option(:url, is_type: :mandatory)}
77
- case options.get_option(:auth_style, is_type: :mandatory)
72
+ rest_params = {base_url: options.get_option(:url, mandatory: true)}
73
+ case options.get_option(:auth_style, mandatory: true)
78
74
  when :arg_pass
79
75
  rest_params[:auth] = {
80
76
  type: :url,
81
77
  url_creds: {
82
- 'login' => options.get_option(:username, is_type: :mandatory),
83
- 'password' => options.get_option(:password, is_type: :mandatory) }}
78
+ 'login' => options.get_option(:username, mandatory: true),
79
+ 'password' => options.get_option(:password, mandatory: true) }}
84
80
  when :head_basic
85
81
  rest_params[:auth] = {
86
82
  type: :basic,
87
- username: options.get_option(:username, is_type: :mandatory),
88
- password: options.get_option(:password, is_type: :mandatory) }
83
+ username: options.get_option(:username, mandatory: true),
84
+ password: options.get_option(:password, mandatory: true) }
89
85
  when :apikey
90
86
  raise 'Not implemented'
91
87
  end
@@ -150,11 +146,12 @@ module Aspera
150
146
  call_params = {format: :json}
151
147
  override_accept = nil
152
148
  # set external parameters if any
153
- self.options.get_option(:params, is_type: :mandatory).each do |name, value|
149
+ # TODO: make not an option, but a parameter
150
+ self.options.get_option(:params, mandatory: true).each do |name, value|
154
151
  call_params["external_parameters[#{name}]"] = value
155
152
  end
156
153
  # synchronous call ?
157
- call_params['synchronous'] = true if self.options.get_option(:synchronous, is_type: :mandatory)
154
+ call_params['synchronous'] = true if self.options.get_option(:synchronous, mandatory: true)
158
155
  # expected result for synchro call ?
159
156
  expected = self.options.get_option(:result)
160
157
  unless expected.nil?
@@ -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.set_obj_attr(:skip_format, self, :option_skip_format, []) # no skip
58
- options.set_obj_attr(:folder_reset_cache, self, :option_folder_reset_cache, :no)
59
- options.set_obj_attr(:skip_types, self, :option_skip_types)
60
- options.set_obj_attr(:previews_folder, self, :option_previews_folder, DEFAULT_PREVIEWS_FOLDER)
61
- options.set_obj_attr(:skip_folders, self, :option_skip_folders, []) # no skip
62
- options.set_obj_attr(:overwrite, self, :option_overwrite, :mtime)
63
- options.set_obj_attr(:file_access, self, :option_file_access, :local)
64
- options.add_opt_list(:skip_format, Aspera::Preview::Generator::PREVIEW_FORMATS, 'skip this preview format (multiple possible)')
65
- options.add_opt_list(:folder_reset_cache, %i[no header read], 'force detection of generated preview by refresh cache')
66
- options.add_opt_simple(:skip_types, 'skip types in comma separated list')
67
- options.add_opt_simple(:previews_folder, 'preview folder in storage root')
68
- options.add_opt_simple(:temp_folder, 'path to temp folder')
69
- options.add_opt_simple(:skip_folders, 'list of folder to skip')
70
- options.add_opt_simple(:case, 'basename of output for for test')
71
- options.add_opt_simple(:scan_path, 'subpath in folder id to start scan in (default=/)')
72
- options.add_opt_simple(:scan_id, 'folder id in storage to start scan in, default is access key main folder id')
73
- options.add_opt_boolean(:mimemagic, 'use Mime type detection of gem mimemagic')
74
- options.add_opt_list(:overwrite, %i[always never mtime], 'when to overwrite result file')
75
- options.add_opt_list(:file_access, %i[local remote], 'how to read and write files in repository')
76
- options.set_option(:temp_folder, Dir.tmpdir)
77
- options.set_option(:mimemagic, false)
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
- options.set_obj_attr(opt[:name], @gen_options, opt[:name], opt[:default])
82
- if opt.key?(:values)
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
- options.add_opt_boolean(opt[:name], opt[:description])
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, is_type: :mandatory), "#{TMP_DIR_PREFIX}.#{SecureRandom.uuid}")
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
@@ -149,8 +148,8 @@ module Aspera
149
148
  if event['data']['direction'].eql?(Fasp::TransferSpec::DIRECTION_RECEIVE) &&
150
149
  event['data']['status'].eql?('completed') &&
151
150
  event['data']['error_code'].eql?(0) &&
152
- event['data'].dig('tags', 'aspera', PREV_GEN_TAG).nil?
153
- folder_id = event.dig('data', 'tags', 'aspera', 'node', 'file_id')
151
+ event['data'].dig('tags', Fasp::TransferSpec::TAG_RESERVED, PREV_GEN_TAG).nil?
152
+ folder_id = event.dig('data', 'tags', Fasp::TransferSpec::TAG_RESERVED, 'node', 'file_id')
154
153
  folder_id ||= event.dig('data', 'file_id')
155
154
  if !folder_id.nil?
156
155
  folder_entry = @api_node.read("files/#{folder_id}")[:data] rescue nil
@@ -206,34 +205,12 @@ module Aspera
206
205
  end
207
206
 
208
207
  def do_transfer(direction, folder_id, source_filename, destination='/')
209
- raise 'error' if destination.nil? && direction.eql?(Fasp::TransferSpec::DIRECTION_RECEIVE)
210
- if @default_transfer_spec.nil?
211
- # make a dummy call to get some default transfer parameters
212
- res = @api_node.create('files/upload_setup', {'transfer_requests' => [{'transfer_request' => {'paths' => [{}], 'destination_root' => '/'}}]})
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
- 'aspera' => {
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(options.get_option(:value))
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, is_type: :mandatory)
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, is_type: :mandatory)
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, is_type: :mandatory),
488
- options.get_option(:username, is_type: :mandatory)
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
@@ -13,9 +13,21 @@ module Aspera
13
13
  module Cli
14
14
  module Plugins
15
15
  # implement basic remote access with FASP/SSH
16
+ class SyncSpecServer
17
+ def initialize(transfer_spec)
18
+ @transfer_spec = transfer_spec
19
+ end
20
+
21
+ def transfer_spec(direction, local_path, remote_path)
22
+ return @transfer_spec
23
+ end
24
+ end
25
+
16
26
  class Server < Aspera::Cli::BasicAuthPlugin
17
27
  SSH_SCHEME = 'ssh'
18
- URI_SCHEMES = %w[https local].push(SSH_SCHEME).freeze
28
+ LOCAL_SCHEME = 'local'
29
+ HTTPS_SCHEME = 'https'
30
+ URI_SCHEMES = [SSH_SCHEME, LOCAL_SCHEME, HTTPS_SCHEME].freeze
19
31
  ASCMD_ALIASES = {
20
32
  browse: :ls,
21
33
  delete: :rm,
@@ -31,7 +43,7 @@ module Aspera
31
43
  cmd = cmd.map{|v|%Q("#{v}")}.join(' ') if cmd.is_a?(Array)
32
44
  Log.log.debug{"Executing: #{cmd} with '#{line}'"}
33
45
  stdout_str, stderr_str, status = Open3.capture3(cmd, stdin_data: line, binmode: true)
34
- Log.log.debug(">> #{status} -> #{stderr_str}")
46
+ Log.log.debug{"exec status: #{status} -> #{stderr_str}"}
35
47
  raise "command #{cmd} failed with code #{status.exitstatus} #{stderr_str}" unless status.success?
36
48
  return stdout_str
37
49
  end
@@ -39,28 +51,32 @@ module Aspera
39
51
 
40
52
  def initialize(env)
41
53
  super(env)
42
- options.add_opt_simple(:ssh_keys, 'SSH key path list (Array or single)')
43
- options.add_opt_simple(:ssh_options, 'SSH options (Hash)')
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: {})
44
57
  options.parse_options!
45
- @ssh_opts = nil
58
+ @ssh_opts = options.get_option(:ssh_options).symbolize_keys
46
59
  end
47
60
 
48
61
  # Read command line options
49
62
  # @return [Hash] transfer specification
50
63
  def options_to_base_transfer_spec
51
- url = options.get_option(:url, is_type: :mandatory)
64
+ url = options.get_option(:url, mandatory: true)
52
65
  server_transfer_spec = {}
53
66
  server_uri = URI.parse(url)
54
- Log.log.debug{"URI : #{server_uri}, port=#{server_uri.port}, scheme:#{server_uri.scheme}"}
67
+ Log.log.debug{"URI=#{server_uri}, host=#{server_uri.hostname}, port=#{server_uri.port}, scheme=#{server_uri.scheme}"}
55
68
  server_transfer_spec['remote_host'] = server_uri.hostname
56
69
  unless URI_SCHEMES.include?(server_uri.scheme)
57
70
  Log.log.warn{"Scheme [#{server_uri.scheme}] not supported in #{url}, use one of: #{URI_SCHEMES.join(', ')}. Defaulting to #{SSH_SCHEME}."}
58
71
  server_uri.scheme = SSH_SCHEME
59
72
  end
60
- if server_uri.scheme.eql?('local')
73
+ if server_uri.scheme.eql?(LOCAL_SCHEME)
61
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'
62
78
  return server_transfer_spec
63
- elsif transfer.option_transfer_spec['token'].is_a?(String) && server_uri.scheme.eql?('https')
79
+ elsif transfer.option_transfer_spec['token'].is_a?(String) && server_uri.scheme.eql?(HTTPS_SCHEME)
64
80
  server_transfer_spec['wss_enabled'] = true
65
81
  server_transfer_spec['wss_port'] = server_uri.port
66
82
  # Using WSS
@@ -78,11 +94,7 @@ module Aspera
78
94
  options.set_option(:username, Aspera::Fasp::TransferSpec::ACCESS_KEY_TRANSFER_USER)
79
95
  Log.log.info{"No username provided: Assuming default transfer user: #{Aspera::Fasp::TransferSpec::ACCESS_KEY_TRANSFER_USER}"}
80
96
  end
81
- server_transfer_spec['remote_user'] = options.get_option(:username, is_type: :mandatory)
82
- ssh_args = options.get_option(:ssh_options)
83
- ssh_args = {} if ssh_args.nil?
84
- raise 'expecting a Hash for ssh_options' unless ssh_args.is_a?(Hash)
85
- @ssh_opts = ssh_args.symbolize_keys
97
+ server_transfer_spec['remote_user'] = options.get_option(:username, mandatory: true)
86
98
  if !server_uri.port.nil?
87
99
  @ssh_opts[:port] = server_uri.port
88
100
  server_transfer_spec['ssh_port'] = server_uri.port
@@ -109,6 +121,11 @@ module Aspera
109
121
  cred_set = true
110
122
  end
111
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
112
129
  # if user provided transfer spec has a token, we will use bypass keys
113
130
  cred_set = true if transfer.option_transfer_spec['token'].is_a?(String)
114
131
  raise 'Either password, key , or transfer spec token must be provided' if !cred_set
@@ -121,7 +138,7 @@ module Aspera
121
138
  Fasp::TransferSpec.action_to_direction(transfer_spec, command)
122
139
  return Main.result_transfer(transfer.start(transfer_spec))
123
140
  when :sync
124
- sync_plugin = Sync.new(@agents, transfer_spec: transfer_spec)
141
+ sync_plugin = Plugins::Sync.new(@agents, sync_spec: SyncSpecServer.new(transfer_spec))
125
142
  return sync_plugin.execute_action
126
143
  end
127
144
  end
@@ -133,7 +150,7 @@ module Aspera
133
150
 
134
151
  def execute_action
135
152
  server_transfer_spec = options_to_base_transfer_spec
136
- ascmd_executor = if !@ssh_opts.nil?
153
+ ascmd_executor = if !@ssh_opts.empty?
137
154
  Ssh.new(server_transfer_spec['remote_host'], server_transfer_spec['remote_user'], @ssh_opts)
138
155
  elsif server_transfer_spec.key?('wss_enabled')
139
156
  nil
@@ -26,24 +26,25 @@ module Aspera
26
26
 
27
27
  def initialize(env)
28
28
  super(env)
29
- options.add_opt_list(:type, %i[any local ldap saml], 'Type of user/group for operations')
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 repository admin].freeze
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, is_type: :mandatory) + '/node_api')
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, is_type: :mandatory)
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 share_permissions app_authorizations].freeze
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
- entity_command = options.get_next_command(entity_action)
84
- entity_path = "#{entities_path}/#{instance_identifier}" if %i[app_authorizations share_permissions].include?(entity_command)
85
- case entity_command
86
- when :list, :show, :create, :delete, :modify
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(entity_command, api_shares_admin, entities_path, display_fields: display_fields)
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
- parameters = options.get_option(:value, is_type: :mandatory)
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
- parameters = options.get_option(:value)
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))
@@ -11,14 +11,14 @@ module Aspera
11
11
  module Plugins
12
12
  # Execute Aspera Sync
13
13
  class Sync < Aspera::Cli::Plugin
14
- def initialize(env, transfer_spec: nil)
14
+ def initialize(env, sync_spec: nil)
15
15
  super(env)
16
- options.add_opt_simple(:sync_info, 'Information for sync instance and sessions (Hash)')
17
- options.add_opt_simple(:sync_session, 'Name of session to use for admin commands. default: first in parameters')
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, is_type: :mandatory)
21
- Aspera::Sync.update_parameters_with_transfer_spec(@params, transfer_spec) unless transfer_spec.nil?
20
+ @params = options.get_option(:sync_info, mandatory: true)
21
+ @sync_spec = sync_spec
22
22
  end
23
23
 
24
24
  ACTIONS = %i[start admin].freeze
@@ -27,7 +27,7 @@ module Aspera
27
27
  command = options.get_next_command(ACTIONS)
28
28
  case command
29
29
  when :start
30
- Aspera::Sync.new(@params).start
30
+ Aspera::Sync.new(@params, @sync_spec).start
31
31
  return Main.result_success
32
32
  when :admin
33
33
  sync_admin = Aspera::SyncAdmin.new(@params, options.get_option(:sync_session))