aspera-cli 4.25.0.pre → 4.25.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 (63) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +23 -17
  4. data/CONTRIBUTING.md +119 -47
  5. data/README.md +325 -239
  6. data/lib/aspera/agent/direct.rb +14 -12
  7. data/lib/aspera/agent/factory.rb +9 -6
  8. data/lib/aspera/agent/transferd.rb +8 -8
  9. data/lib/aspera/api/aoc.rb +33 -24
  10. data/lib/aspera/api/ats.rb +1 -0
  11. data/lib/aspera/api/faspex.rb +11 -5
  12. data/lib/aspera/ascmd.rb +1 -1
  13. data/lib/aspera/ascp/installation.rb +7 -7
  14. data/lib/aspera/ascp/management.rb +9 -5
  15. data/lib/aspera/assert.rb +3 -3
  16. data/lib/aspera/cli/extended_value.rb +10 -2
  17. data/lib/aspera/cli/formatter.rb +15 -62
  18. data/lib/aspera/cli/manager.rb +9 -43
  19. data/lib/aspera/cli/plugins/aoc.rb +71 -66
  20. data/lib/aspera/cli/plugins/ats.rb +30 -36
  21. data/lib/aspera/cli/plugins/base.rb +11 -6
  22. data/lib/aspera/cli/plugins/config.rb +21 -16
  23. data/lib/aspera/cli/plugins/console.rb +2 -1
  24. data/lib/aspera/cli/plugins/faspex.rb +7 -4
  25. data/lib/aspera/cli/plugins/faspex5.rb +12 -9
  26. data/lib/aspera/cli/plugins/faspio.rb +5 -2
  27. data/lib/aspera/cli/plugins/httpgw.rb +2 -1
  28. data/lib/aspera/cli/plugins/node.rb +10 -6
  29. data/lib/aspera/cli/plugins/oauth.rb +12 -11
  30. data/lib/aspera/cli/plugins/orchestrator.rb +2 -1
  31. data/lib/aspera/cli/plugins/preview.rb +2 -2
  32. data/lib/aspera/cli/plugins/server.rb +3 -2
  33. data/lib/aspera/cli/plugins/shares.rb +59 -20
  34. data/lib/aspera/cli/transfer_agent.rb +1 -2
  35. data/lib/aspera/cli/version.rb +1 -1
  36. data/lib/aspera/command_line_builder.rb +5 -5
  37. data/lib/aspera/coverage.rb +5 -1
  38. data/lib/aspera/dot_container.rb +108 -0
  39. data/lib/aspera/environment.rb +69 -89
  40. data/lib/aspera/faspex_postproc.rb +1 -1
  41. data/lib/aspera/id_generator.rb +7 -10
  42. data/lib/aspera/keychain/macos_security.rb +2 -2
  43. data/lib/aspera/log.rb +2 -1
  44. data/lib/aspera/oauth/base.rb +25 -38
  45. data/lib/aspera/oauth/factory.rb +5 -6
  46. data/lib/aspera/oauth/generic.rb +1 -1
  47. data/lib/aspera/oauth/jwt.rb +1 -1
  48. data/lib/aspera/oauth/url_json.rb +4 -3
  49. data/lib/aspera/oauth/web.rb +2 -2
  50. data/lib/aspera/preview/file_types.rb +1 -1
  51. data/lib/aspera/preview/terminal.rb +95 -29
  52. data/lib/aspera/preview/utils.rb +6 -5
  53. data/lib/aspera/rest.rb +5 -2
  54. data/lib/aspera/ssh.rb +6 -5
  55. data/lib/aspera/sync/conf.schema.yaml +2 -2
  56. data/lib/aspera/sync/operations.rb +3 -3
  57. data/lib/aspera/transfer/parameters.rb +6 -6
  58. data/lib/aspera/transfer/spec.schema.yaml +4 -4
  59. data/lib/aspera/transfer/spec_doc.rb +11 -21
  60. data/lib/aspera/uri_reader.rb +17 -3
  61. data.tar.gz.sig +0 -0
  62. metadata +17 -2
  63. metadata.gz.sig +0 -0
@@ -32,6 +32,7 @@ require 'aspera/assert'
32
32
  require 'aspera/oauth'
33
33
  require 'aspera/ssl'
34
34
  require 'openssl'
35
+ require 'digest'
35
36
  require 'open3'
36
37
  require 'date'
37
38
  require 'erb'
@@ -80,7 +81,7 @@ module Aspera
80
81
  # We need to defer parsing of options until we have the config file, so we can use @extend with @preset
81
82
  super
82
83
  @use_plugin_defaults = true
83
- @config_presets = nil
84
+ @config_presets = {}
84
85
  @config_checksum_on_disk = nil
85
86
  @vault_instance = nil
86
87
  @pac_exec = nil
@@ -493,8 +494,9 @@ module Aspera
493
494
  end
494
495
  end
495
496
 
497
+ # @return [Integer]
496
498
  def config_checksum
497
- JSON.generate(@config_presets).hash
499
+ Digest::SHA1.hexdigest(JSON.generate(@config_presets))
498
500
  end
499
501
 
500
502
  # Read config file and validate format
@@ -535,6 +537,7 @@ module Aspera
535
537
  Log.log.warn{"#{file} -> #{@main_folder}"}
536
538
  end
537
539
  end
540
+ return
538
541
  rescue Psych::SyntaxError => e
539
542
  Log.log.error('YAML error in config file')
540
543
  raise e
@@ -584,6 +587,15 @@ module Aspera
584
587
  return Main.result_status("Opened: #{one_link['href']}")
585
588
  end
586
589
  end
590
+ Aspera.error_unreachable_line
591
+ end
592
+
593
+ def install_transfer_sdk
594
+ # Reset to default location, if older default was used
595
+ Products::Transferd.sdk_directory = self.class.default_app_main_folder(app_name: TRANSFERD_APP_NAME) if @sdk_default_location
596
+ asked_version = options.get_next_argument('transferd version', mandatory: false)
597
+ name, version, folder = Ascp::Installation.instance.install_sdk(url: options.get_option(:sdk_url, mandatory: true), version: asked_version)
598
+ return Main.result_status("Installed #{name} version #{version} in #{folder}")
587
599
  end
588
600
 
589
601
  def execute_action_ascp
@@ -621,11 +633,7 @@ module Aspera
621
633
  return Main.result_nothing
622
634
  end
623
635
  when :install
624
- # Reset to default location, if older default was used
625
- Products::Transferd.sdk_directory = self.class.default_app_main_folder(app_name: TRANSFERD_APP_NAME) if @sdk_default_location
626
- version = options.get_next_argument('transferd version', mandatory: false)
627
- n, v = Ascp::Installation.instance.install_sdk(url: options.get_option(:sdk_url, mandatory: true), version: version)
628
- return Main.result_status("Installed #{n} version #{v}")
636
+ return install_transfer_sdk
629
637
  when :spec
630
638
  fields, data = Transfer::SpecDoc.man_table(Formatter, include_option: true)
631
639
  return Main.result_object_list(data, fields: fields.map(&:to_s))
@@ -650,11 +658,7 @@ module Aspera
650
658
  command = options.get_next_command(%i[list install])
651
659
  case command
652
660
  when :install
653
- # Reset to default location, if older default was used
654
- Products::Transferd.sdk_directory = self.class.default_app_main_folder(app_name: TRANSFERD_APP_NAME) if @sdk_default_location
655
- version = options.get_next_argument('transferd version', mandatory: false)
656
- n, v = Ascp::Installation.instance.install_sdk(url: options.get_option(:sdk_url, mandatory: true), version: version)
657
- return Main.result_status("Installed #{n} version #{v}")
661
+ return install_transfer_sdk
658
662
  when :list
659
663
  sdk_list = Ascp::Installation.instance.sdk_locations
660
664
  return Main.result_object_list(
@@ -1166,16 +1170,17 @@ module Aspera
1166
1170
  end
1167
1171
 
1168
1172
  # Lookup the corresponding secret for the given URL and usernames
1169
- # @raise Exception if mandatory and not found
1170
- def lookup_secret(url:, username:, mandatory: false)
1173
+ # @param url [String] Server URL
1174
+ # @param username [String] Username
1175
+ # @return [String, nil] Secret if found
1176
+ def lookup_secret(url:, username:)
1171
1177
  secret = options.get_option(:secret)
1172
- if secret.nil?
1178
+ if secret.eql?('PRESET')
1173
1179
  conf = lookup_preset(url: url, username: username)
1174
1180
  if conf.is_a?(Hash)
1175
1181
  Log.log.debug{"Found preset #{conf} with URL and username"}
1176
1182
  secret = conf['password']
1177
1183
  end
1178
- raise "Please provide secret for #{username} using option: secret or by setting a preset for #{username}@#{url}." if secret.nil? && mandatory
1179
1184
  end
1180
1185
  return secret
1181
1186
  end
@@ -13,6 +13,7 @@ module Aspera
13
13
  private_constant :STANDARD_PATH, :DEFAULT_FILTER_AGE_SECONDS, :EXPR_RE
14
14
 
15
15
  class << self
16
+ # @return [Hash,NilClass]
16
17
  def detect(address_or_url)
17
18
  address_or_url = "https://#{address_or_url}" unless address_or_url.match?(%r{^[a-z]{1,6}://})
18
19
  urls = [address_or_url]
@@ -52,7 +53,7 @@ module Aspera
52
53
  end
53
54
 
54
55
  # @param wizard [Wizard] The wizard object
55
- # @param app_url [Wizard] The wizard object
56
+ # @param app_url [String] Tested URL
56
57
  # @return [Hash] :preset_value, :test_args
57
58
  def wizard(wizard, app_url)
58
59
  return {
@@ -39,6 +39,7 @@ module Aspera
39
39
  private_constant :KEY_NODE, :KEY_PATH, :PACKAGE_MATCH_FIELD, :ATOM_MAILBOXES, :ATOM_PARAMS, :ATOM_EXT_PARAMS, :PUB_LINK_EXTERNAL_MATCH, :HEADER_FASPEX_VERSION
40
40
 
41
41
  class << self
42
+ # @return [Hash,NilClass]
42
43
  def detect(address_or_url)
43
44
  address_or_url = "https://#{address_or_url}" unless address_or_url.match?(%r{^[a-z]{1,6}://})
44
45
  urls = [address_or_url]
@@ -108,7 +109,7 @@ module Aspera
108
109
  end
109
110
 
110
111
  # @param wizard [Wizard] The wizard object
111
- # @param app_url [Wizard] The wizard object
112
+ # @param app_url [String] Tested URL
112
113
  # @return [Hash] :preset_value, :test_args
113
114
  def wizard(wizard, app_url)
114
115
  return {
@@ -149,7 +150,9 @@ module Aspera
149
150
  grant_method: :generic,
150
151
  base_url: "#{faspex_api_base}/auth/oauth2",
151
152
  auth: {type: :basic, username: options.get_option(:username, mandatory: true), password: options.get_option(:password, mandatory: true)},
152
- scope: 'admin',
153
+ params: {
154
+ scope: 'admin'
155
+ },
153
156
  grant_type: 'password'
154
157
  }
155
158
  )
@@ -336,12 +339,12 @@ module Aspera
336
339
  skip_ids_persistency = PersistencyActionOnce.new(
337
340
  manager: persistency,
338
341
  data: skip_ids_data,
339
- id: IdGenerator.from_list([
342
+ id: IdGenerator.from_list(
340
343
  'faspex_recv',
341
344
  options.get_option(:url, mandatory: true),
342
345
  options.get_option(:username, mandatory: true),
343
346
  options.get_option(:box, mandatory: true).to_s
344
- ])
347
+ )
345
348
  )
346
349
  end
347
350
  # get command line parameters
@@ -23,6 +23,7 @@ module Aspera
23
23
  'Faspex'
24
24
  end
25
25
 
26
+ # @return [Hash,NilClass]
26
27
  def detect(address_or_url)
27
28
  # add scheme if missing
28
29
  address_or_url = "https://#{address_or_url}" unless address_or_url.match?(%r{^[a-z]{1,6}://})
@@ -52,7 +53,7 @@ module Aspera
52
53
  end
53
54
 
54
55
  # @param wizard [Wizard] The wizard object
55
- # @param app_url [Wizard] The wizard object
56
+ # @param app_url [String] Tested URL
56
57
  # @return [Hash] :preset_value, :test_args
57
58
  def wizard(wizard, app_url)
58
59
  client_id = options.get_option(:client_id)
@@ -78,12 +79,14 @@ module Aspera
78
79
  )
79
80
  return {
80
81
  preset_value: {
81
- url: app_url,
82
- username: wiz_username,
83
- auth: :jwt.to_s,
84
- private_key: "@file:#{private_key_path}",
85
- client_id: client_id,
86
- client_secret: client_secret
82
+ url: app_url,
83
+ username: wiz_username,
84
+ auth: :jwt.to_s,
85
+ private_key: "@file:#{private_key_path}",
86
+ params: {
87
+ client_id: client_id,
88
+ client_secret: client_secret
89
+ }
87
90
  },
88
91
  test_args: 'user profile show'
89
92
  }
@@ -197,12 +200,12 @@ module Aspera
197
200
  skip_ids_persistency = PersistencyActionOnce.new(
198
201
  manager: persistency,
199
202
  data: [],
200
- id: IdGenerator.from_list([
203
+ id: IdGenerator.from_list(
201
204
  'faspex_recv',
202
205
  options.get_option(:url, mandatory: true),
203
206
  options.get_option(:username, mandatory: true),
204
207
  options.get_option(:box, mandatory: true)
205
- ])
208
+ )
206
209
  )
207
210
  end
208
211
  packages = []
@@ -13,6 +13,7 @@ module Aspera
13
13
  'faspio Gateway'
14
14
  end
15
15
 
16
+ # @return [Hash,NilClass]
16
17
  def detect(base_url)
17
18
  api = Rest.new(base_url: base_url)
18
19
  data, http = api.read('ping', ret: :both)
@@ -27,7 +28,7 @@ module Aspera
27
28
  end
28
29
 
29
30
  # @param wizard [Wizard] The wizard object
30
- # @param app_url [Wizard] The wizard object
31
+ # @param app_url [String] Tested URL
31
32
  # @return [Hash] :preset_value, :test_args
32
33
  def wizard(wizard, app_url)
33
34
  return {
@@ -63,7 +64,9 @@ module Aspera
63
64
  type: :oauth2,
64
65
  grant_method: :jwt,
65
66
  base_url: "#{base_url}/auth",
66
- client_id: app_client_id,
67
+ params: {
68
+ client_id: app_client_id
69
+ },
67
70
  use_query: true,
68
71
  payload: {
69
72
  iss: app_client_id, # issuer
@@ -14,6 +14,7 @@ module Aspera
14
14
  'HTTP Gateway'
15
15
  end
16
16
 
17
+ # @return [Hash,NilClass]
17
18
  def detect(base_url)
18
19
  api = Api::Httpgw.new(url: base_url)
19
20
  api_info = api.info
@@ -26,7 +27,7 @@ module Aspera
26
27
  end
27
28
 
28
29
  # @param wizard [Wizard] The wizard object
29
- # @param app_url [Wizard] The wizard object
30
+ # @param app_url [String] Tested URL
30
31
  # @return [Hash] :preset_value, :test_args
31
32
  def wizard(wizard, app_url)
32
33
  return {
@@ -54,6 +54,7 @@ module Aspera
54
54
  'HSTS Node API'
55
55
  end
56
56
 
57
+ # @return [Hash,NilClass]
57
58
  def detect(address_or_url)
58
59
  urls = if address_or_url.match?(%r{^[a-z]{1,6}://})
59
60
  [address_or_url]
@@ -414,10 +415,12 @@ module Aspera
414
415
  root_file_id = options.get_option(:root_id)
415
416
  if root_file_id.nil?
416
417
  ak_info = @api_node.read("access_keys/#{access_key_id}")
418
+ ak_secret = config.lookup_secret(url: @api_node.base_url, username: ak_info['id'])
417
419
  # change API credentials if different access key
418
420
  if !access_key_id.eql?('self')
421
+ Aspera.assert(ak_secret, type: Cli::MissingArgument){"Please provide secret for #{ak_info['id']} using option: secret or by setting a preset for #{ak_info['id']}@#{@api_node.base_url}."}
419
422
  @api_node.auth_params[:username] = ak_info['id']
420
- @api_node.auth_params[:password] = config.lookup_secret(url: @api_node.base_url, username: ak_info['id'], mandatory: true)
423
+ @api_node.auth_params[:password] = ak_secret
421
424
  end
422
425
  root_file_id = ak_info['root_file_id']
423
426
  end
@@ -516,7 +519,8 @@ module Aspera
516
519
  else Aspera.error_unreachable_line
517
520
  end
518
521
  return Main.result_single_object(result) if command_repo.eql?(:node_info)
519
- raise BadArgument, 'Cannot get bearer token if authenticating with secret' unless apifid[:api].auth_params[:type].eql?(:oauth2)
522
+ Log.dump(:result, result)
523
+ raise BadArgument, "Cannot get bearer token if authenticating with secret (#{apifid[:api].auth_params[:type]})" unless apifid[:api].auth_params[:type].eql?(:oauth2)
520
524
  Aspera.assert(OAuth::Factory.bearer_auth?(result[:password])){'Not using bearer token auth'}
521
525
  return Main.result_text(result[:password])
522
526
  when :browse
@@ -727,12 +731,12 @@ module Aspera
727
731
  skip_ids_persistency = PersistencyActionOnce.new(
728
732
  manager: persistency,
729
733
  data: iteration_data,
730
- id: IdGenerator.from_list([
734
+ id: IdGenerator.from_list(
731
735
  'sync_files',
732
736
  options.get_option(:url, mandatory: true),
733
737
  options.get_option(:username, mandatory: true),
734
738
  async_id
735
- ])
739
+ )
736
740
  )
737
741
  data.select!{ |l| l['fnid'].to_i > iteration_data.first} unless iteration_data.first.nil?
738
742
  iteration_data[0] = data.last['fnid'].to_i unless data.empty?
@@ -872,11 +876,11 @@ module Aspera
872
876
  iteration_persistency = PersistencyActionOnce.new(
873
877
  manager: persistency,
874
878
  data: [],
875
- id: IdGenerator.from_list([
879
+ id: IdGenerator.from_list(
876
880
  'node_transfers',
877
881
  options.get_option(:url, mandatory: true),
878
882
  options.get_option(:username, mandatory: true)
879
- ])
883
+ )
880
884
  )
881
885
  if transfer_filter.delete('reset')
882
886
  iteration_persistency.data.clear
@@ -10,7 +10,7 @@ module Aspera
10
10
  # OAuth methods supported
11
11
  AUTH_TYPES = %i[web jwt boot].freeze
12
12
  # Options used for authentication
13
- AUTH_OPTIONS = %i[url auth client_id client_secret scope redirect_uri private_key passphrase username password].freeze
13
+ AUTH_OPTIONS = %i[url auth client_id client_secret redirect_uri private_key passphrase username password].freeze
14
14
  def initialize(**_)
15
15
  super
16
16
  options.declare(:auth, 'OAuth type of authentication', allowed: AUTH_TYPES, default: :jwt)
@@ -19,22 +19,23 @@ module Aspera
19
19
  options.declare(:redirect_uri, 'OAuth (Web) redirect URI for web authentication')
20
20
  options.declare(:private_key, 'OAuth (JWT) RSA private key PEM value (prefix file path with @file:)')
21
21
  options.declare(:passphrase, 'OAuth (JWT) RSA private key passphrase')
22
- options.declare(:scope, 'OAuth scope for API calls')
23
22
  end
24
23
 
25
- # Get all options specified by AUTH_OPTIONS and add.keys
26
- # Adds those not nil to the `base`.
24
+ # Get command line options specified by `AUTH_OPTIONS` and `option.keys` (value is default).
25
+ # Adds those not nil to the `kwargs`.
27
26
  # Instantiate the provided `klass` with those kwargs.
28
- # `add` can specify a default value (not `nil`)
29
- # @param klass [Class] API object to create
30
- # @param base [Hash] The base options for creation
31
- # @param add [Hash] Additional options, key=symbol, value:default value or nil
32
- def new_with_options(klass, base: {}, add: {})
27
+ # `option` can specify a default value (not `nil`)
28
+ # @param klass [Class] API object to create
29
+ # @param kwargs [Hash] The fixed keyword arguments for creation
30
+ # @param option [Hash] Additional options, key=symbol, value:default value or nil
31
+ # @return [Object] instance of `klass`
32
+ # @raise [Cli::Error] if a required option is missing
33
+ def new_with_options(klass, kwargs: {}, option: {})
33
34
  klass.new(**
34
- (AUTH_OPTIONS + add.keys).each_with_object(base) do |i, m|
35
+ (AUTH_OPTIONS + option.keys).each_with_object(kwargs) do |i, m|
35
36
  v = options.get_option(i)
36
37
  m[i] = v unless v.nil?
37
- m[i] = add[i] unless !m[i].nil? || add[i].nil?
38
+ m[i] = option[i] unless !m[i].nil? || option[i].nil?
38
39
  end)
39
40
  rescue ::ArgumentError => e
40
41
  if (m = e.message.match(/missing keyword: :(.*)$/))
@@ -16,6 +16,7 @@ module Aspera
16
16
  TEST_ENDPOINT = 'api/remote_node_ping'
17
17
  private_constant :STANDARD_PATH, :TEST_ENDPOINT
18
18
  class << self
19
+ # @return [Hash,NilClass]
19
20
  def detect(address_or_url)
20
21
  address_or_url = "https://#{address_or_url}" unless address_or_url.match?(%r{^[a-z]{1,6}://})
21
22
  urls = [address_or_url]
@@ -41,7 +42,7 @@ module Aspera
41
42
  end
42
43
 
43
44
  # @param wizard [Wizard] The wizard object
44
- # @param app_url [Wizard] The wizard object
45
+ # @param app_url [String] Tested URL
45
46
  # @return [Hash] :preset_value, :test_args
46
47
  def wizard(wizard, app_url)
47
48
  return {
@@ -451,12 +451,12 @@ module Aspera
451
451
  iteration_persistency = PersistencyActionOnce.new(
452
452
  manager: persistency,
453
453
  data: [],
454
- id: IdGenerator.from_list([
454
+ id: IdGenerator.from_list(
455
455
  'preview_iteration',
456
456
  command.to_s,
457
457
  options.get_option(:url, mandatory: true),
458
458
  options.get_option(:username, mandatory: true)
459
- ])
459
+ )
460
460
  )
461
461
  end
462
462
  # call processing method specified by command line command
@@ -32,7 +32,7 @@ module Aspera
32
32
 
33
33
  class LocalExecutor
34
34
  def execute(ascmd_path, input:)
35
- return Environment.secure_capture(exec: ascmd_path, stdin_data: input, binmode: true, exception: false)
35
+ return Environment.secure_execute(ascmd_path, mode: :capture, stdin_data: input, binmode: true, exception: false)
36
36
  end
37
37
  end
38
38
 
@@ -41,6 +41,7 @@ module Aspera
41
41
  'HSTS Fasp/SSH'
42
42
  end
43
43
 
44
+ # @return [Hash,NilClass]
44
45
  def detect(address_or_url)
45
46
  urls = if address_or_url.match?(%r{^[a-z]{1,6}://})
46
47
  [address_or_url]
@@ -70,7 +71,7 @@ module Aspera
70
71
  end
71
72
 
72
73
  # @param wizard [Wizard] The wizard object
73
- # @param app_url [Wizard] The wizard object
74
+ # @param app_url [String] Tested URL
74
75
  # @return [Hash] :preset_value, :test_args
75
76
  def wizard(wizard, app_url)
76
77
  return {
@@ -13,27 +13,62 @@ module Aspera
13
13
  # path for node admin after base url
14
14
  ADMIN_API_PATH = 'api/v1'
15
15
  class << self
16
+ # Check various endpoints on Shares
17
+ # @return [Hash] with version, ping, api
18
+ def health_check(url)
19
+ result = {}
20
+ result[:version] =
21
+ begin
22
+ version = nil
23
+ login_page = Rest
24
+ .new(base_url: url, redirect_max: 2)
25
+ .read('', headers: {'Accept'=>'text/html'})
26
+ if (m = login_page.match(/\(v([0-9a-f\.]+)\)/))
27
+ version = m[1]
28
+ if (m = login_page.match(/Patch level ([0-9]+)/))
29
+ version = "#{result[:version]} #{m[0]}"
30
+ end
31
+ end
32
+ raise 'no version' if version.nil?
33
+ version
34
+ rescue => e
35
+ e
36
+ end
37
+ result[:ping] =
38
+ begin
39
+ Rest
40
+ .new(base_url: "#{url}/#{NODE_API_PATH}")
41
+ .read('ping', headers: {'Content-Type'=>'application/json'})
42
+ 'ping ok'
43
+ rescue => e
44
+ e
45
+ end
46
+ result[:api] =
47
+ begin
48
+ resp = Rest.new(base_url: url, redirect_max: 1).read("#{NODE_API_PATH}/app", exception: false, ret: :resp)
49
+ # shall fail: shares requires auth, but we check error message
50
+ raise 'not found' unless resp.code.to_s.eql?('401') && resp.body.eql?('{"error":{"user_message":"API user authentication failed"}}')
51
+ 'available'
52
+ rescue => e
53
+ e
54
+ end
55
+ result
56
+ end
57
+
58
+ # @return [Hash,NilClass]
16
59
  def detect(address_or_url)
17
60
  address_or_url = "https://#{address_or_url}" unless address_or_url.match?(%r{^[a-z]{1,6}://})
18
- api = Rest.new(base_url: address_or_url, redirect_max: 1)
19
- # TODO: use ping instead ?
20
- resp = api.read("#{NODE_API_PATH}/app", exception: false, ret: :resp)
21
- # shall fail: shares requires auth, but we check error message
22
- return unless resp.code.to_s.eql?('401') && resp.body.eql?('{"error":{"user_message":"API user authentication failed"}}')
23
- version = 'unknown'
24
- http = api.read('login', headers: {'Accept'=>'*/*'}, ret: :resp)
25
- if (m = http.body.match(/\(v(1\..*)\)/))
26
- version = m[1]
27
- end
61
+ health = health_check(address_or_url)
62
+ return unless health[:api].is_a?(String)
28
63
  return {
29
- version: version,
64
+ version: health[:version].is_a?(String) ? health[:version] : 'unknown',
30
65
  url: address_or_url
31
66
  }
32
67
  end
33
68
  end
34
69
 
35
70
  # @param wizard [Wizard] The wizard object
36
- # @param app_url [Wizard] The wizard object
71
+ # @param app_url [String] Tested URL
37
72
  # @return [Hash] :preset_value, :test_args
38
73
  def wizard(wizard, app_url)
39
74
  return {
@@ -62,14 +97,18 @@ module Aspera
62
97
  case command
63
98
  when :health
64
99
  nagios = Nagios.new
65
- begin
66
- http = Rest
67
- .new(base_url: "#{options.get_option(:url, mandatory: true)}/#{NODE_API_PATH}")
68
- .read('ping', ret: :resp)
69
- raise Error, 'Shares not detected' unless http.body.eql?(' ')
70
- nagios.add_ok('shares api', 'accessible')
71
- rescue StandardError => e
72
- nagios.add_critical('API', e.to_s)
100
+ shares_url = options.get_option(:url, mandatory: true)
101
+ health = self.class.health_check(shares_url)
102
+ nagios.add_ok('version', health[:version]) if health[:version].is_a?(String)
103
+ if health[:ping].is_a?(String)
104
+ nagios.add_ok('ping', health[:ping])
105
+ else
106
+ nagios.add_critical('ping', health[:ping].to_s)
107
+ end
108
+ if health[:api].is_a?(String)
109
+ nagios.add_ok('API', health[:api])
110
+ else
111
+ nagios.add_critical('API', health[:api].to_s)
73
112
  end
74
113
  Main.result_object_list(nagios.status_list)
75
114
  when :files
@@ -31,7 +31,6 @@ module Aspera
31
31
  :FILE_LIST_FROM_TRANSFER_SPEC,
32
32
  :FILE_LIST_OPTIONS,
33
33
  :DEFAULT_TRANSFER_NOTIFY_TEMPLATE
34
- TRANSFER_AGENTS = Agent::Factory.instance.list.freeze
35
34
 
36
35
  class << self
37
36
  # @return :success if all sessions statuses returned by "start" are success
@@ -65,7 +64,7 @@ module Aspera
65
64
  @opt_mgr.declare(:to_folder, 'Destination folder for transferred files')
66
65
  @opt_mgr.declare(:sources, "How list of transferred files is provided (#{FILE_LIST_OPTIONS.join(',')})", default: FILE_LIST_FROM_ARGS)
67
66
  @opt_mgr.declare(:src_type, 'Type of file list', allowed: %i[list pair], default: :list)
68
- @opt_mgr.declare(:transfer, 'Type of transfer agent', allowed: TRANSFER_AGENTS, default: :direct)
67
+ @opt_mgr.declare(:transfer, 'Type of transfer agent', allowed: Agent::Factory::ALL.keys, default: :direct)
69
68
  @opt_mgr.declare(:transfer_info, 'Parameters for transfer agent', allowed: Hash, handler: {o: self, m: :transfer_info})
70
69
  @opt_mgr.parse_options!
71
70
  @notification_cb = nil
@@ -4,6 +4,6 @@ module Aspera
4
4
  module Cli
5
5
  # For beta add extension : .beta1
6
6
  # For dev version add extension : .pre
7
- VERSION = '4.25.0.pre'
7
+ VERSION = '4.25.0'
8
8
  end
9
9
  end
@@ -22,11 +22,11 @@ module Aspera
22
22
  'x-cli-envvar', # [String] Name of env var
23
23
  'x-cli-option', # [String] Command line option (starts with "-")
24
24
  'x-cli-short', # [String] Command line option (starts with "-")
25
- 'x-cli-switch', # [Bool] `true` if option has no arg, else by default option has a value
26
- 'x-cli-special', # [Bool] `true` if special handling (deferred)
25
+ 'x-cli-switch', # [Boolean] `true` if option has no arg, else by default option has a value
26
+ 'x-cli-special', # [Boolean] `true` if special handling (deferred)
27
27
  'x-cli-convert', # [String,Hash] Method name for Convert object or Conversion for enum ts to arg
28
28
  'x-agents', # [Array] Supported agents (for doc only), if not specified: all
29
- 'x-ts-name', # [Bool,String] (async) true if same name in transfer spec, else real name in transfer spec, else ignored
29
+ 'x-ts-name', # [Boolean,String] (async) true if same name in transfer spec, else real name in transfer spec, else ignored
30
30
  'x-ts-convert', # [String] (async) Name of methods to convert value from transfer spec to `conf` API.
31
31
  'x-deprecation' # [String] Deprecation message for doc
32
32
  ].freeze
@@ -50,8 +50,8 @@ module Aspera
50
50
  private
51
51
 
52
52
  # Fill default values for some fields in the schema
53
- # @param schema [Hash] The JSON schema
54
- # @param ascp [Bool] `true` if ascp
53
+ # @param schema [Hash] The JSON schema
54
+ # @param ascp [Boolean] `true` if ascp
55
55
  def validate_schema(schema, ascp: false)
56
56
  direct_props = %w[x-cli-option x-cli-envvar x-cli-special].freeze
57
57
  schema['properties'].each do |name, info|
@@ -6,14 +6,18 @@ if ENV.key?('ENABLE_COVERAGE')
6
6
  require 'securerandom'
7
7
  # compute development top folder based on this source location
8
8
  development_root = File.dirname(File.realpath(__FILE__), 3)
9
+ coverage_dir = 'tmp/coverage'
10
+ coverage_root = File.join(development_root, coverage_dir)
11
+ FileUtils.mkdir_p(coverage_root)
9
12
  SimpleCov.root(development_root)
13
+ SimpleCov.coverage_dir(coverage_dir)
10
14
  SimpleCov.enable_for_subprocesses if SimpleCov.respond_to?(:enable_for_subprocesses)
11
15
  # keep cache data for 1 day (must be longer than time to run the whole test suite)
12
16
  SimpleCov.merge_timeout(86400)
13
17
  SimpleCov.command_name(SecureRandom.uuid)
14
18
  SimpleCov.at_exit do
15
19
  original_file_descriptor = $stdout
16
- $stdout.reopen(File.join(development_root, 'simplecov.log'))
20
+ $stdout.reopen(File.open(File.join(coverage_root, 'simplecov.log'), 'a'))
17
21
  SimpleCov.result.format!
18
22
  $stdout.reopen(original_file_descriptor)
19
23
  end