aspera-cli 4.21.1 → 4.22.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 (105) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/BUGS.md +1 -1
  4. data/CHANGELOG.md +52 -22
  5. data/CONTRIBUTING.md +69 -148
  6. data/README.md +929 -668
  7. data/bin/ascli +5 -14
  8. data/bin/asession +1 -3
  9. data/examples/get_proto_file.rb +4 -3
  10. data/examples/proxy.pac +20 -20
  11. data/lib/aspera/agent/base.rb +11 -5
  12. data/lib/aspera/agent/connect.rb +30 -28
  13. data/lib/aspera/agent/{alpha.rb → desktop.rb} +35 -31
  14. data/lib/aspera/agent/direct.rb +141 -121
  15. data/lib/aspera/agent/httpgw.rb +22 -26
  16. data/lib/aspera/agent/node.rb +14 -11
  17. data/lib/aspera/agent/transferd.rb +30 -19
  18. data/lib/aspera/api/alee.rb +1 -1
  19. data/lib/aspera/api/aoc.rb +6 -6
  20. data/lib/aspera/api/cos_node.rb +2 -2
  21. data/lib/aspera/api/httpgw.rb +7 -3
  22. data/lib/aspera/api/node.rb +10 -8
  23. data/lib/aspera/ascmd.rb +3 -3
  24. data/lib/aspera/ascp/installation.rb +53 -72
  25. data/lib/aspera/ascp/management.rb +1 -1
  26. data/lib/aspera/assert.rb +11 -2
  27. data/lib/aspera/cli/error.rb +2 -2
  28. data/lib/aspera/cli/extended_value.rb +46 -21
  29. data/lib/aspera/cli/formatter.rb +55 -48
  30. data/lib/aspera/cli/hints.rb +1 -1
  31. data/lib/aspera/cli/info.rb +1 -0
  32. data/lib/aspera/cli/main.rb +192 -170
  33. data/lib/aspera/cli/manager.rb +18 -18
  34. data/lib/aspera/cli/plugin.rb +23 -20
  35. data/lib/aspera/cli/plugin_factory.rb +1 -1
  36. data/lib/aspera/cli/plugins/alee.rb +1 -1
  37. data/lib/aspera/cli/plugins/aoc.rb +247 -159
  38. data/lib/aspera/cli/plugins/ats.rb +19 -17
  39. data/lib/aspera/cli/plugins/config.rb +76 -113
  40. data/lib/aspera/cli/plugins/console.rb +5 -3
  41. data/lib/aspera/cli/plugins/faspex.rb +39 -35
  42. data/lib/aspera/cli/plugins/faspex5.rb +111 -84
  43. data/lib/aspera/cli/plugins/faspio.rb +13 -1
  44. data/lib/aspera/cli/plugins/httpgw.rb +13 -1
  45. data/lib/aspera/cli/plugins/node.rb +312 -182
  46. data/lib/aspera/cli/plugins/orchestrator.rb +34 -40
  47. data/lib/aspera/cli/plugins/preview.rb +3 -3
  48. data/lib/aspera/cli/plugins/server.rb +6 -6
  49. data/lib/aspera/cli/plugins/shares.rb +5 -5
  50. data/lib/aspera/cli/sync_actions.rb +19 -18
  51. data/lib/aspera/cli/transfer_agent.rb +5 -5
  52. data/lib/aspera/cli/transfer_progress.rb +2 -2
  53. data/lib/aspera/cli/version.rb +1 -1
  54. data/lib/aspera/command_line_builder.rb +116 -95
  55. data/lib/aspera/coverage.rb +8 -5
  56. data/lib/aspera/environment.rb +26 -17
  57. data/lib/aspera/faspex_gw.rb +14 -14
  58. data/lib/aspera/faspex_postproc.rb +10 -11
  59. data/lib/aspera/hash_ext.rb +4 -14
  60. data/lib/aspera/json_rpc.rb +1 -1
  61. data/lib/aspera/keychain/encrypted_hash.rb +47 -34
  62. data/lib/aspera/keychain/factory.rb +41 -0
  63. data/lib/aspera/keychain/hashicorp_vault.rb +71 -0
  64. data/lib/aspera/keychain/macos_security.rb +19 -11
  65. data/lib/aspera/log.rb +28 -34
  66. data/lib/aspera/nagios.rb +6 -6
  67. data/lib/aspera/node_simulator.rb +8 -8
  68. data/lib/aspera/oauth/base.rb +14 -7
  69. data/lib/aspera/oauth/factory.rb +5 -6
  70. data/lib/aspera/oauth/url_json.rb +6 -6
  71. data/lib/aspera/persistency_action_once.rb +6 -4
  72. data/lib/aspera/persistency_folder.rb +2 -2
  73. data/lib/aspera/preview/generator.rb +13 -10
  74. data/lib/aspera/preview/options.rb +16 -16
  75. data/lib/aspera/preview/terminal.rb +4 -4
  76. data/lib/aspera/preview/utils.rb +15 -17
  77. data/lib/aspera/products/connect.rb +35 -1
  78. data/lib/aspera/products/{alpha.rb → desktop.rb} +3 -3
  79. data/lib/aspera/products/transferd.rb +9 -2
  80. data/lib/aspera/proxy_auto_config.rb +2 -2
  81. data/lib/aspera/rest.rb +56 -47
  82. data/lib/aspera/rest_errors_aspera.rb +1 -1
  83. data/lib/aspera/secret_hider.rb +12 -5
  84. data/lib/aspera/ssh.rb +4 -4
  85. data/lib/aspera/temp_file_manager.rb +5 -4
  86. data/lib/aspera/transfer/convert.rb +29 -0
  87. data/lib/aspera/transfer/error_info.rb +66 -66
  88. data/lib/aspera/transfer/parameters.rb +13 -68
  89. data/lib/aspera/transfer/spec.rb +5 -6
  90. data/lib/aspera/transfer/spec.schema.yaml +753 -0
  91. data/lib/aspera/transfer/spec_doc.rb +62 -0
  92. data/lib/aspera/transfer/sync.rb +23 -72
  93. data/lib/aspera/transfer/sync_instance.schema.yaml +13 -0
  94. data/lib/aspera/transfer/sync_session.schema.yaml +79 -0
  95. data/lib/aspera/transfer/uri.rb +6 -6
  96. data/lib/aspera/uri_reader.rb +18 -1
  97. data/lib/aspera/web_auth.rb +1 -1
  98. data/lib/aspera/web_server_simple.rb +53 -44
  99. data.tar.gz.sig +0 -0
  100. metadata +28 -165
  101. metadata.gz.sig +0 -0
  102. data/examples/build_exec +0 -74
  103. data/examples/build_exec_rubyc +0 -40
  104. data/examples/build_package.sh +0 -28
  105. data/lib/aspera/transfer/spec.yaml +0 -718
@@ -15,8 +15,8 @@ module Aspera
15
15
  # keys in hash that contain secrets
16
16
  KEY_SECRETS = %w[password secret passphrase _key apikey crn token].freeze
17
17
  HTTP_SECRETS = %w[Authorization].freeze
18
- ALL_SECRETS = [ASCP_ENV_SECRETS, KEY_SECRETS, HTTP_SECRETS].flatten.freeze
19
- ALL_SECRETS2 = [KEY_SECRETS, HTTP_SECRETS].flatten.freeze
18
+ ALL_SECRETS = (ASCP_ENV_SECRETS + KEY_SECRETS + HTTP_SECRETS).freeze
19
+ ALL_SECRETS2 = (KEY_SECRETS + HTTP_SECRETS).freeze
20
20
  KEY_FALSE_POSITIVES = [/^access_key$/, /^fallback_private_key$/].freeze
21
21
  # regex that define named captures :begin and :end
22
22
  REGEX_LOG_REPLACES = [
@@ -38,6 +38,7 @@ module Aspera
38
38
  class << self
39
39
  attr_accessor :log_secrets
40
40
 
41
+ # @return new log formatter that hides secrets
41
42
  def log_formatter(original_formatter)
42
43
  original_formatter ||= Logger::Formatter.new
43
44
  # NOTE: that @log_secrets may be set AFTER this init is done, so it's done at runtime
@@ -51,21 +52,27 @@ module Aspera
51
52
  end
52
53
  end
53
54
 
55
+ def hide_secrets_in_string(value)
56
+ return value.gsub(REGEX_LOG_REPLACES.first){"#{Regexp.last_match(:begin)}#{HIDDEN_PASSWORD}#{Regexp.last_match(:end)}"}
57
+ end
58
+
59
+ # @return true if the key denotes a secret
54
60
  def secret?(keyword, value)
55
61
  keyword = keyword.to_s if keyword.is_a?(Symbol)
56
62
  # only Strings can be secrets, not booleans, or hash, arrays
57
63
  return false unless keyword.is_a?(String) && value.is_a?(String)
58
64
  # those are not secrets
59
- return false if KEY_FALSE_POSITIVES.any?{|f|f.match?(keyword)}
65
+ return false if KEY_FALSE_POSITIVES.any?{ |f| f.match?(keyword)}
60
66
  return true if ADDITIONAL_KEYS_TO_HIDE.include?(keyword)
61
67
  # check if keyword (name) contains an element that designate it as a secret
62
- ALL_SECRETS.any?{|kw|keyword.include?(kw)}
68
+ ALL_SECRETS.any?{ |kw| keyword.include?(kw)}
63
69
  end
64
70
 
71
+ # Hides recursively secrets in Hash or Array of Hash
65
72
  def deep_remove_secret(obj)
66
73
  case obj
67
74
  when Array
68
- obj.each{|i|deep_remove_secret(i)}
75
+ obj.each{ |i| deep_remove_secret(i)}
69
76
  when Hash
70
77
  obj.each do |k, v|
71
78
  if secret?(k, v)
data/lib/aspera/ssh.rb CHANGED
@@ -24,8 +24,8 @@ module Aspera
24
24
 
25
25
  def disable_ecd_sha2_algorithms
26
26
  Log.log.debug('Disabling SSH ecdsa')
27
- Net::SSH::Transport::Algorithms::ALGORITHMS.each_value { |a| a.reject! { |a| a =~ /^ecd(sa|h)-sha2/ } }
28
- Net::SSH::KnownHosts::SUPPORTED_TYPE.reject! { |t| t =~ /^ecd(sa|h)-sha2/ }
27
+ Net::SSH::Transport::Algorithms::ALGORITHMS.each_value{ |a| a.reject!{ |a| a =~ /^ecd(sa|h)-sha2/}}
28
+ Net::SSH::KnownHosts::SUPPORTED_TYPE.reject!{ |t| t =~ /^ecd(sa|h)-sha2/}
29
29
  end
30
30
  end
31
31
  # ssh_options: same as Net::SSH.start
@@ -49,7 +49,7 @@ module Aspera
49
49
  Net::SSH.start(@host, @username, @ssh_options) do |session|
50
50
  ssh_channel = session.open_channel do |channel|
51
51
  # prepare stdout processing
52
- channel.on_data{|_chan, data|response.push(data)}
52
+ channel.on_data{ |_chan, data| response.push(data)}
53
53
  # prepare stderr processing, stderr if type = 1
54
54
  channel.on_extended_data do |_chan, _type, data|
55
55
  error_message = "#{cmd}: [#{data.chomp}]"
@@ -58,7 +58,7 @@ module Aspera
58
58
  raise error_message
59
59
  end
60
60
  # send command to SSH channel (execute) cspell: disable-next-line
61
- channel.send('cexe'.reverse, cmd){|_ch, _success|channel.send_data(input) unless input.nil?}
61
+ channel.send('cexe'.reverse, cmd){ |_ch, _success| channel.send_data(input) unless input.nil?}
62
62
  end
63
63
  # wait for channel to finish (command exit)
64
64
  ssh_channel.wait
@@ -35,22 +35,23 @@ module Aspera
35
35
 
36
36
  # ensure that provided folder exists, or create it, generate a unique filename
37
37
  # @return path to that unique file
38
- def new_file_path_in_folder(temp_folder, add_base = '')
38
+ def new_file_path_in_folder(temp_folder, prefix: nil, suffix: nil)
39
39
  FileUtils.mkdir_p(temp_folder)
40
- new_file = File.join(temp_folder, add_base + SecureRandom.uuid)
40
+ new_file = File.join(temp_folder, [prefix, SecureRandom.uuid, suffix].compact.join('-'))
41
41
  @created_files.push(new_file)
42
42
  new_file
43
43
  end
44
44
 
45
45
  # same as above but in global temp folder, with user's name
46
- def new_file_path_global(base_name)
46
+ def new_file_path_global(prefix=nil, suffix: nil)
47
47
  username =
48
48
  begin
49
49
  Etc.getlogin || Etc.getpwuid(Process.uid).name || 'unknown_user'
50
50
  rescue StandardError
51
51
  'unknown_user'
52
52
  end
53
- new_file_path_in_folder(Etc.systmpdir, base_name + '_' + username + '_')
53
+ prefix = [prefix, username].compact.join('-')
54
+ new_file_path_in_folder(Etc.systmpdir, prefix: prefix, suffix: suffix)
54
55
  end
55
56
 
56
57
  def cleanup_expired(temp_folder)
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'aspera/assert'
4
+ module Aspera
5
+ module Transfer
6
+ # concertion class for transfert spec values to CLI values (ascp)
7
+ class Convert
8
+ class << self
9
+ # special encoding methods used in YAML (key: convert)
10
+ def remove_hyphen(value); value.tr('-', ''); end
11
+
12
+ # special encoding methods used in YAML (key: convert)
13
+ def json64(value); Base64.strict_encode64(JSON.generate(value)); end
14
+
15
+ # special encoding methods used in YAML (key: convert)
16
+ def base64(value); Base64.strict_encode64(value); end
17
+
18
+ # transform yes/no to true/false
19
+ def yes_to_true(value)
20
+ case value
21
+ when 'yes' then return true
22
+ when 'no' then return false
23
+ else Aspera.error_unexpected_value(value){'only: yes or no: '}
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -10,81 +10,81 @@ module Aspera
10
10
  # rubocop:disable Layout/FirstHashElementLineBreak
11
11
  ERROR_INFO = {
12
12
  # id retry-able mnemo message additional info
13
- 1 => { r: false, c: 'FASP_PROTO', m: 'Generic fasp(tm) protocol error', a: 'fasp(tm) error'},
14
- 2 => { r: false, c: 'ASCP', m: 'Generic SCP error', a: 'ASCP error'},
15
- 3 => { r: false, c: 'AMBIGUOUS_TARGET', m: 'Target incorrectly specified', a: 'Ambiguous target'},
16
- 4 => { r: false, c: 'NO_SUCH_FILE', m: 'No such file or directory', a: 'No such file or directory'},
17
- 5 => { r: false, c: 'NO_PERMS', m: 'Insufficient permission to read or write', a: 'Insufficient permissions'},
18
- 6 => { r: false, c: 'NOT_DIR', m: 'Target is not a directory', a: 'Target must be a directory'},
19
- 7 => { r: false, c: 'IS_DIR', m: 'File is a directory - expected regular file', a: 'Expected regular file'},
20
- 8 => { r: false, c: 'USAGE', m: 'Incorrect usage of scp command', a: 'Incorrect usage of Aspera scp command'},
21
- 9 => { r: false, c: 'LIC_DUP', m: 'Duplicate license', a: 'Duplicate license'},
22
- 10 => { r: false, c: 'LIC_RATE_EXCEEDED', m: 'Rate exceeds the cap imposed by license', a: 'Rate exceeds cap imposed by license'},
23
- 11 => { r: false, c: 'INTERNAL_ERROR', m: 'Internal error (unexpected error)', a: 'Internal error'},
24
- 12 => { r: true, c: 'TRANSFER_ERROR', m: 'Error establishing control connection',
13
+ 1 => {r: false, c: 'FASP_PROTO', m: 'Generic fasp(tm) protocol error', a: 'fasp(tm) error'},
14
+ 2 => {r: false, c: 'ASCP', m: 'Generic SCP error', a: 'ASCP error'},
15
+ 3 => {r: false, c: 'AMBIGUOUS_TARGET', m: 'Target incorrectly specified', a: 'Ambiguous target'},
16
+ 4 => {r: false, c: 'NO_SUCH_FILE', m: 'No such file or directory', a: 'No such file or directory'},
17
+ 5 => {r: false, c: 'NO_PERMS', m: 'Insufficient permission to read or write', a: 'Insufficient permissions'},
18
+ 6 => {r: false, c: 'NOT_DIR', m: 'Target is not a directory', a: 'Target must be a directory'},
19
+ 7 => {r: false, c: 'IS_DIR', m: 'File is a directory - expected regular file', a: 'Expected regular file'},
20
+ 8 => {r: false, c: 'USAGE', m: 'Incorrect usage of scp command', a: 'Incorrect usage of Aspera scp command'},
21
+ 9 => {r: false, c: 'LIC_DUP', m: 'Duplicate license', a: 'Duplicate license'},
22
+ 10 => {r: false, c: 'LIC_RATE_EXCEEDED', m: 'Rate exceeds the cap imposed by license', a: 'Rate exceeds cap imposed by license'},
23
+ 11 => {r: false, c: 'INTERNAL_ERROR', m: 'Internal error (unexpected error)', a: 'Internal error'},
24
+ 12 => {r: true, c: 'TRANSFER_ERROR', m: 'Error establishing control connection',
25
25
  a: 'Error establishing SSH connection (check SSH port and firewall)'},
26
- 13 => { r: true, c: 'TRANSFER_TIMEOUT', m: 'Timeout establishing control connection',
26
+ 13 => {r: true, c: 'TRANSFER_TIMEOUT', m: 'Timeout establishing control connection',
27
27
  a: 'Timeout establishing SSH connection (check SSH port and firewall)'},
28
- 14 => { r: true, c: 'CONNECTION_ERROR', m: 'Error establishing data connection',
28
+ 14 => {r: true, c: 'CONNECTION_ERROR', m: 'Error establishing data connection',
29
29
  a: 'Error establishing UDP connection (check UDP port and firewall)'},
30
- 15 => { r: true, c: 'CONNECTION_TIMEOUT', m: 'Timeout establishing data connection',
30
+ 15 => {r: true, c: 'CONNECTION_TIMEOUT', m: 'Timeout establishing data connection',
31
31
  a: 'Timeout establishing UDP connection (check UDP port and firewall)'},
32
- 16 => { r: true, c: 'CONNECTION_LOST', m: 'Connection lost', a: 'Connection lost'},
33
- 17 => { r: true, c: 'RCVR_SEND_ERROR', m: 'Receiver fails to send feedback', a: 'Network failure (receiver can\'t send feedback)'},
34
- 18 => { r: true, c: 'RCVR_RECV_ERROR', m: 'Receiver fails to receive data packets', a: 'Network failure (receiver can\'t receive UDP data)'},
35
- 19 => { r: false, c: 'AUTH', m: 'Authentication failure', a: 'Authentication failure'},
36
- 20 => { r: false, c: 'NOTHING', m: 'Nothing to transfer', a: 'Nothing to transfer'},
37
- 21 => { r: false, c: 'NOT_REGULAR', m: 'Not a regular file (special file)', a: 'Not a regular file'},
38
- 22 => { r: false, c: 'FILE_TABLE_OVR', m: 'File table overflow', a: 'File table overflow'},
39
- 23 => { r: true, c: 'TOO_MANY_FILES', m: 'Too many files open', a: 'Too many files open'},
40
- 24 => { r: false, c: 'FILE_TOO_BIG', m: 'File too big for file system', a: 'File too big for filesystem'},
41
- 25 => { r: false, c: 'NO_SPACE_LEFT', m: 'No space left on disk', a: 'No space left on disk'},
42
- 26 => { r: false, c: 'READ_ONLY_FS', m: 'Read only file system', a: 'Read only filesystem'},
43
- 27 => { r: false, c: 'SOME_FILE_ERRS', m: 'Some individual files failed', a: 'One or more files failed'},
44
- 28 => { r: false, c: 'USER_CANCEL', m: 'Cancelled by user', a: 'Cancelled by user'},
45
- 29 => { r: false, c: 'LIC_NOLIC', m: 'License not found or unable to access', a: 'Unable to access license info'},
46
- 30 => { r: false, c: 'LIC_EXPIRED', m: 'License expired', a: 'License expired'},
47
- 31 => { r: false, c: 'SOCK_SETUP', m: 'Unable to setup socket (create, bind, etc ...)', a: 'Unable to set up socket'},
48
- 32 => { r: true, c: 'OUT_OF_MEMORY', m: 'Out of memory, unable to allocate', a: 'Out of memory'},
49
- 33 => { r: true, c: 'THREAD_SPAWN', m: 'Can\'t spawn thread', a: 'Unable to spawn thread'},
50
- 34 => { r: false, c: 'UNAUTHORIZED', m: 'Unauthorized by external auth server', a: 'Unauthorized'},
51
- 35 => { r: true, c: 'DISK_READ', m: 'Error reading source file from disk', a: 'Disk read error'},
52
- 36 => { r: true, c: 'DISK_WRITE', m: 'Error writing to disk', a: 'Disk write error'},
53
- 37 => { r: true, c: 'AUTHORIZATION', m: 'Used interchangeably with ERR_UNAUTHORIZED', a: 'Authorization failure'},
54
- 38 => { r: false, c: 'LIC_ILLEGAL', m: 'Operation not permitted by license', a: 'Operation not permitted by license'},
55
- 39 => { r: true, c: 'PEER_ABORTED_SESSION', m: 'Remote peer terminated session', a: 'Peer aborted session'},
56
- 40 => { r: true, c: 'DATA_TRANSFER_TIMEOUT', m: 'Transfer stalled, timed out', a: 'Data transfer stalled, timed out'},
57
- 41 => { r: false, c: 'BAD_PATH', m: 'Path violates docroot containment', a: 'File location is outside \'docroot\' hierarchy'},
58
- 42 => { r: false, c: 'ALREADY_EXISTS', m: 'File or directory already exists', a: 'File or directory already exists'},
59
- 43 => { r: false, c: 'STAT_FAILS', m: 'Cannot stat file', a: 'Cannot collect details about file or directory'},
60
- 44 => { r: true, c: 'PMTU_BRTT_ERROR', m: 'UDP session initiation fatal error', a: 'UDP session initiation fatal error'},
61
- 45 => { r: true, c: 'BWMEAS_ERROR', m: 'Bandwidth measurement fatal error', a: 'Bandwidth measurement fatal error'},
62
- 46 => { r: false, c: 'VLINK_ERROR', m: 'Virtual link error', a: 'Virtual link error'},
63
- 47 => { r: false, c: 'CONNECTION_ERROR_HTTP', m: 'Error establishing HTTP connection',
32
+ 16 => {r: true, c: 'CONNECTION_LOST', m: 'Connection lost', a: 'Connection lost'},
33
+ 17 => {r: true, c: 'RCVR_SEND_ERROR', m: 'Receiver fails to send feedback', a: 'Network failure (receiver can\'t send feedback)'},
34
+ 18 => {r: true, c: 'RCVR_RECV_ERROR', m: 'Receiver fails to receive data packets', a: 'Network failure (receiver can\'t receive UDP data)'},
35
+ 19 => {r: false, c: 'AUTH', m: 'Authentication failure', a: 'Authentication failure'},
36
+ 20 => {r: false, c: 'NOTHING', m: 'Nothing to transfer', a: 'Nothing to transfer'},
37
+ 21 => {r: false, c: 'NOT_REGULAR', m: 'Not a regular file (special file)', a: 'Not a regular file'},
38
+ 22 => {r: false, c: 'FILE_TABLE_OVR', m: 'File table overflow', a: 'File table overflow'},
39
+ 23 => {r: true, c: 'TOO_MANY_FILES', m: 'Too many files open', a: 'Too many files open'},
40
+ 24 => {r: false, c: 'FILE_TOO_BIG', m: 'File too big for file system', a: 'File too big for filesystem'},
41
+ 25 => {r: false, c: 'NO_SPACE_LEFT', m: 'No space left on disk', a: 'No space left on disk'},
42
+ 26 => {r: false, c: 'READ_ONLY_FS', m: 'Read only file system', a: 'Read only filesystem'},
43
+ 27 => {r: false, c: 'SOME_FILE_ERRS', m: 'Some individual files failed', a: 'One or more files failed'},
44
+ 28 => {r: false, c: 'USER_CANCEL', m: 'Cancelled by user', a: 'Cancelled by user'},
45
+ 29 => {r: false, c: 'LIC_NOLIC', m: 'License not found or unable to access', a: 'Unable to access license info'},
46
+ 30 => {r: false, c: 'LIC_EXPIRED', m: 'License expired', a: 'License expired'},
47
+ 31 => {r: false, c: 'SOCK_SETUP', m: 'Unable to setup socket (create, bind, etc ...)', a: 'Unable to set up socket'},
48
+ 32 => {r: true, c: 'OUT_OF_MEMORY', m: 'Out of memory, unable to allocate', a: 'Out of memory'},
49
+ 33 => {r: true, c: 'THREAD_SPAWN', m: 'Can\'t spawn thread', a: 'Unable to spawn thread'},
50
+ 34 => {r: false, c: 'UNAUTHORIZED', m: 'Unauthorized by external auth server', a: 'Unauthorized'},
51
+ 35 => {r: true, c: 'DISK_READ', m: 'Error reading source file from disk', a: 'Disk read error'},
52
+ 36 => {r: true, c: 'DISK_WRITE', m: 'Error writing to disk', a: 'Disk write error'},
53
+ 37 => {r: true, c: 'AUTHORIZATION', m: 'Used interchangeably with ERR_UNAUTHORIZED', a: 'Authorization failure'},
54
+ 38 => {r: false, c: 'LIC_ILLEGAL', m: 'Operation not permitted by license', a: 'Operation not permitted by license'},
55
+ 39 => {r: true, c: 'PEER_ABORTED_SESSION', m: 'Remote peer terminated session', a: 'Peer aborted session'},
56
+ 40 => {r: true, c: 'DATA_TRANSFER_TIMEOUT', m: 'Transfer stalled, timed out', a: 'Data transfer stalled, timed out'},
57
+ 41 => {r: false, c: 'BAD_PATH', m: 'Path violates docroot containment', a: 'File location is outside \'docroot\' hierarchy'},
58
+ 42 => {r: false, c: 'ALREADY_EXISTS', m: 'File or directory already exists', a: 'File or directory already exists'},
59
+ 43 => {r: false, c: 'STAT_FAILS', m: 'Cannot stat file', a: 'Cannot collect details about file or directory'},
60
+ 44 => {r: true, c: 'PMTU_BRTT_ERROR', m: 'UDP session initiation fatal error', a: 'UDP session initiation fatal error'},
61
+ 45 => {r: true, c: 'BWMEAS_ERROR', m: 'Bandwidth measurement fatal error', a: 'Bandwidth measurement fatal error'},
62
+ 46 => {r: false, c: 'VLINK_ERROR', m: 'Virtual link error', a: 'Virtual link error'},
63
+ 47 => {r: false, c: 'CONNECTION_ERROR_HTTP', m: 'Error establishing HTTP connection',
64
64
  a: 'Error establishing HTTP connection (check HTTP port and firewall)'},
65
- 48 => { r: false, c: 'FILE_ENCRYPTION_ERROR', m: 'File encryption error, e.g. corrupt file',
65
+ 48 => {r: false, c: 'FILE_ENCRYPTION_ERROR', m: 'File encryption error, e.g. corrupt file',
66
66
  a: 'File encryption/decryption error, e.g. corrupt file'},
67
- 49 => { r: false, c: 'FILE_DECRYPTION_PASS', m: 'File encryption/decryption error, e.g. corrupt file', a: 'File decryption error, bad passphrase'},
68
- 50 => { r: false, c: 'BAD_CONFIGURATION', m: 'Aspera.conf contains invalid data and was rejected', a: 'Invalid configuration'},
69
- 51 => { r: false, c: 'INSECURE_CONNECTION', m: 'Remote-host key check failure', a: 'Remote host is not who we expected'},
70
- 52 => { r: false, c: 'START_VALIDATION_FAILED', m: 'File start validation failed', a: 'File start validation failed'},
71
- 53 => { r: false, c: 'STOP_VALIDATION_FAILED', m: 'File stop validation failed', a: 'File stop validation failed'},
72
- 54 => { r: false, c: 'THRESHOLD_VALIDATION_FAILED', m: 'File threshold validation failed', a: 'File threshold validation failed'},
73
- 55 => { r: false, c: 'FILEPATH_TOO_LONG', m: 'File path/name too long for underlying file system', a: 'File path exceeds underlying file system limit'},
74
- 56 => { r: false, c: 'ILLEGAL_CHARS_IN_PATH', m: 'Windows path contains illegal characters',
67
+ 49 => {r: false, c: 'FILE_DECRYPTION_PASS', m: 'File encryption/decryption error, e.g. corrupt file', a: 'File decryption error, bad passphrase'},
68
+ 50 => {r: false, c: 'BAD_CONFIGURATION', m: 'Aspera.conf contains invalid data and was rejected', a: 'Invalid configuration'},
69
+ 51 => {r: false, c: 'INSECURE_CONNECTION', m: 'Remote-host key check failure', a: 'Remote host is not who we expected'},
70
+ 52 => {r: false, c: 'START_VALIDATION_FAILED', m: 'File start validation failed', a: 'File start validation failed'},
71
+ 53 => {r: false, c: 'STOP_VALIDATION_FAILED', m: 'File stop validation failed', a: 'File stop validation failed'},
72
+ 54 => {r: false, c: 'THRESHOLD_VALIDATION_FAILED', m: 'File threshold validation failed', a: 'File threshold validation failed'},
73
+ 55 => {r: false, c: 'FILEPATH_TOO_LONG', m: 'File path/name too long for underlying file system', a: 'File path exceeds underlying file system limit'},
74
+ 56 => {r: false, c: 'ILLEGAL_CHARS_IN_PATH', m: 'Windows path contains illegal characters',
75
75
  a: 'Path being written to Windows file system contains illegal characters'},
76
- 57 => { r: false, c: 'CHUNK_MUST_MATCH_ALIGNMENT', m: 'Chunk size/start must be aligned with storage', a: 'Chunk size/start must be aligned with storage'},
77
- 58 => { r: false, c: 'VALIDATION_SESSION_ABORT', m: 'Session aborted to due to validation error', a: 'Session aborted to due validation error'},
78
- 59 => { r: false, c: 'REMOTE_STORAGE_ERROR', m: 'Remote storage errored', a: 'Remote storage errored'},
79
- 60 => { r: false, c: 'LUA_SCRIPT_ABORTED_SESSION', m: 'Session aborted due to Lua script abort', a: 'Session aborted due to Lua script abort'},
80
- 61 => { r: true, c: 'SSEAR_RETRYABLE', m: 'Transfer failed because of a retryable Encryption at Rest error',
76
+ 57 => {r: false, c: 'CHUNK_MUST_MATCH_ALIGNMENT', m: 'Chunk size/start must be aligned with storage', a: 'Chunk size/start must be aligned with storage'},
77
+ 58 => {r: false, c: 'VALIDATION_SESSION_ABORT', m: 'Session aborted to due to validation error', a: 'Session aborted to due validation error'},
78
+ 59 => {r: false, c: 'REMOTE_STORAGE_ERROR', m: 'Remote storage errored', a: 'Remote storage errored'},
79
+ 60 => {r: false, c: 'LUA_SCRIPT_ABORTED_SESSION', m: 'Session aborted due to Lua script abort', a: 'Session aborted due to Lua script abort'},
80
+ 61 => {r: true, c: 'SSEAR_RETRYABLE', m: 'Transfer failed because of a retryable Encryption at Rest error',
81
81
  a: 'Transfer failed because of a retryable Encryption at Rest error'},
82
- 62 => { r: false, c: 'SSEAR_FATAL', m: 'Transfer failed because of a fatal Encryption at Rest error',
82
+ 62 => {r: false, c: 'SSEAR_FATAL', m: 'Transfer failed because of a fatal Encryption at Rest error',
83
83
  a: 'Transfer failed because of a fatal Encryption at Rest error'},
84
- 63 => { r: false, c: 'LINK_LOOP', m: 'Path refers to a symbolic link loop', a: 'Path refers to a symbolic link loop'},
85
- 64 => { r: false, c: 'CANNOT_RENAME_PARTIAL_FILES', m: 'Can\'t rename a partial file', a: 'Can\'t rename a partial file.'},
86
- 65 => { r: false, c: 'CIPHER_NON_COMPAT_FIPS', m: 'Can\'t use this cipher with FIPS mode enabled', a: 'Can\'t use this cipher with FIPS mode enabled'},
87
- 66 => { r: false, c: 'PEER_REQUIRES_FIPS', m: 'Peer rejects cipher due to FIPS mode enabled on peer',
84
+ 63 => {r: false, c: 'LINK_LOOP', m: 'Path refers to a symbolic link loop', a: 'Path refers to a symbolic link loop'},
85
+ 64 => {r: false, c: 'CANNOT_RENAME_PARTIAL_FILES', m: 'Can\'t rename a partial file', a: 'Can\'t rename a partial file.'},
86
+ 65 => {r: false, c: 'CIPHER_NON_COMPAT_FIPS', m: 'Can\'t use this cipher with FIPS mode enabled', a: 'Can\'t use this cipher with FIPS mode enabled'},
87
+ 66 => {r: false, c: 'PEER_REQUIRES_FIPS', m: 'Peer rejects cipher due to FIPS mode enabled on peer',
88
88
  a: 'Peer rejects cipher due to FIPS mode enabled on peer'}
89
89
  }.freeze
90
90
  # rubocop:enable Layout/MultilineHashKeyLineBreaks
@@ -6,9 +6,9 @@ require 'aspera/command_line_builder'
6
6
  require 'aspera/temp_file_manager'
7
7
  require 'aspera/transfer/error'
8
8
  require 'aspera/transfer/spec'
9
+ require 'aspera/transfer/convert'
9
10
  require 'aspera/ascp/installation'
10
11
  require 'aspera/cli/formatter'
11
- require 'aspera/agent/base'
12
12
  require 'aspera/rest'
13
13
  require 'securerandom'
14
14
  require 'base64'
@@ -20,15 +20,10 @@ module Aspera
20
20
  module Transfer
21
21
  # translate transfer specification to ascp parameter list
22
22
  class Parameters
23
- # Agents shown in manual for parameters (sub list)
24
- SUPPORTED_AGENTS = Agent::Base.agent_list.freeze
25
23
  FILE_LIST_OPTIONS = ['--file-list', '--file-pair-list'].freeze
26
- # Short names of columns in manual
27
- SUPPORTED_AGENTS_SHORT = SUPPORTED_AGENTS.map{|agent_sym|agent_sym.to_s[0].to_sym}
24
+ private_constant :FILE_LIST_OPTIONS
28
25
  HTTP_FALLBACK_ACTIVATION_VALUES = ['1', 1, true, 'force'].freeze
29
26
 
30
- private_constant :SUPPORTED_AGENTS, :FILE_LIST_OPTIONS
31
-
32
27
  class << self
33
28
  # temp file list files are created here
34
29
  def file_list_folder=(value)
@@ -46,64 +41,14 @@ module Aspera
46
41
  @file_list_folder ||= TempFileManager.instance.new_file_path_global('asession_filelists')
47
42
  end
48
43
 
49
- # @param formatter [Cli::Formatter] formatter to use
50
- # @return a table suitable to display in manual
51
- def man_table(formatter)
52
- result = []
53
- Spec::DESCRIPTION.each do |name, options|
54
- param = {name: name, type: [options[:accepted_types]].flatten.join(','), description: options[:desc]}
55
- # add flags for supported agents in doc
56
- SUPPORTED_AGENTS.each do |agent_sym|
57
- param[agent_sym.to_s[0].to_sym] = Cli::Formatter.tick(options[:agents].nil? || options[:agents].include?(agent_sym))
58
- end
59
- # only keep lines that are usable in supported agents
60
- next if SUPPORTED_AGENTS_SHORT.inject(true){|memory, agent_short_sym|memory && param[agent_short_sym].empty?}
61
- param[:cli] =
62
- case options[:cli][:type]
63
- when :envvar then 'env:' + options[:cli][:variable]
64
- when :opt_without_arg then options[:cli][:switch]
65
- when :opt_with_arg
66
- values = if options.key?(:enum)
67
- ['enum']
68
- elsif options[:accepted_types].is_a?(Array)
69
- options[:accepted_types]
70
- elsif !options[:accepted_types].nil?
71
- [options[:accepted_types]]
72
- else
73
- raise "error: #{param}"
74
- end.map{|n|"{#{n}}"}.join('|')
75
- conversion_tag = options[:cli].key?(:convert) ? '(conversion)' : ''
76
- "#{options[:cli][:switch]} #{conversion_tag}#{values}"
77
- when :special then formatter.special_format('special')
78
- when :ignore then formatter.special_format('ignored')
79
- else
80
- param[:d].eql?(tick_yes) ? '' : 'n/a'
81
- end
82
- param[:description] += "\nAllowed values: #{options[:enum].join(', ')}" if options.key?(:enum)
83
- # replace "solidus" HTML entity with its text value
84
- param[:description] = param[:description].gsub('&sol;', '\\')
85
- result.push(param)
86
- end
87
- return result.sort_by { |parameter_info| parameter_info[:name] }
88
- end
89
-
90
- # special encoding methods used in YAML (key: :convert)
91
- def convert_remove_hyphen(value); value.tr('-', ''); end
92
-
93
- # special encoding methods used in YAML (key: :convert)
94
- def convert_json64(value); Base64.strict_encode64(JSON.generate(value)); end
95
-
96
- # special encoding methods used in YAML (key: :convert)
97
- def convert_base64(value); Base64.strict_encode64(value); end
98
-
99
44
  # file list is provided directly with ascp arguments
100
- # @param ascp_args [Array,NilClass] ascp arguments
45
+ # @columns ascp_args [Array,NilClass] ascp arguments
101
46
  def ascp_args_file_list?(ascp_args)
102
- ascp_args&.any?{|i|FILE_LIST_OPTIONS.include?(i)}
47
+ ascp_args&.any?{ |i| FILE_LIST_OPTIONS.include?(i)}
103
48
  end
104
49
  end
105
50
 
106
- # @param options [Hash] key: :wss: bool, :ascp_args: array of strings
51
+ # @columns options [Hash] key: :wss: bool, :ascp_args: array of strings
107
52
  def initialize(
108
53
  job_spec,
109
54
  ascp_args: nil,
@@ -125,7 +70,7 @@ module Aspera
125
70
  Aspera.assert(@ascp_args.all?(String)){'all ascp arguments must be String'}
126
71
  Aspera.assert_type(@trusted_certs, Array){'trusted_certs'}
127
72
  Aspera.assert_values(@client_ssh_key, Ascp::Installation::CLIENT_SSH_KEY_OPTIONS)
128
- @builder = CommandLineBuilder.new(@job_spec, Spec::DESCRIPTION)
73
+ @builder = CommandLineBuilder.new(@job_spec, Spec::SCHEMA, Convert)
129
74
  end
130
75
 
131
76
  # either place source files on command line, or add file list file
@@ -133,29 +78,29 @@ module Aspera
133
78
  # is the file list provided through ascp parameters?
134
79
  ascp_file_list_provided = self.class.ascp_args_file_list?(@ascp_args)
135
80
  # set if paths is mandatory in ts
136
- @builder.params_definition['paths'][:mandatory] = !@job_spec.key?('keepalive') && !ascp_file_list_provided # cspell:words keepalive
81
+ @builder.required('paths', !@job_spec.key?('keepalive') && !ascp_file_list_provided) # cspell:words keepalive
137
82
  # get paths in transfer spec (after setting if it is mandatory)
138
83
  ts_paths_array = @builder.read_param('paths')
139
84
  file_list_option = nil
140
85
  # transfer spec contains paths ?
141
86
  if !ts_paths_array.nil?
142
87
  Aspera.assert(!ascp_file_list_provided){'file list provided both in transfer spec and ascp file list. Remove one of them.'}
143
- Aspera.assert(ts_paths_array.all?{|i|i.key?('source')}){"All elements of paths must have a 'source' key"}
144
- is_pair_list = ts_paths_array.any?{|i|i.key?('destination')}
145
- raise "All elements of paths must be consistent with 'destination' key" if is_pair_list && !ts_paths_array.all?{|i|i.key?('destination')}
88
+ Aspera.assert(ts_paths_array.all?{ |i| i.key?('source')}){"All elements of paths must have a 'source' key"}
89
+ is_pair_list = ts_paths_array.any?{ |i| i.key?('destination')}
90
+ raise "All elements of paths must be consistent with 'destination' key" if is_pair_list && !ts_paths_array.all?{ |i| i.key?('destination')}
146
91
  if self.class.file_list_folder.nil?
147
92
  Aspera.assert(!is_pair_list){'file pair list is not supported when file list folder is not set'}
148
93
  # not safe for special characters ? (maybe not, depends on OS)
149
94
  Log.log.debug('placing source file list on command line (no file list file)')
150
- @builder.add_command_line_options(ts_paths_array.map{|i|i['source']})
95
+ @builder.add_command_line_options(ts_paths_array.map{ |i| i['source']})
151
96
  else
152
97
  # safer option: generate a file list file if there is storage defined for it
153
98
  if is_pair_list
154
99
  file_list_option = '--file-pair-list'
155
- lines = ts_paths_array.each_with_object([]){|e, m|m.push(e['source'], e['destination']) }
100
+ lines = ts_paths_array.each_with_object([]){ |e, m| m.push(e['source'], e['destination'])}
156
101
  else
157
102
  file_list_option = '--file-list'
158
- lines = ts_paths_array.map{|i|i['source']}
103
+ lines = ts_paths_array.map{ |i| i['source']}
159
104
  end
160
105
  file_list_file = TempFileManager.instance.new_file_path_in_folder(self.class.file_list_folder)
161
106
  Log.log.debug{Log.dump(:file_list, lines)}
@@ -2,7 +2,6 @@
2
2
 
3
3
  require 'aspera/command_line_builder'
4
4
  require 'aspera/assert'
5
- require 'yaml'
6
5
 
7
6
  module Aspera
8
7
  module Transfer
@@ -36,12 +35,12 @@ module Aspera
36
35
  XFER_DIR_TO_TYPE.fetch(direction)
37
36
  end
38
37
  end
39
- DESCRIPTION = CommandLineBuilder.normalize_description(YAML.load_file("#{__FILE__[0..-3]}yaml"))
38
+ SCHEMA = CommandLineBuilder.read_schema(__FILE__)
40
39
  # define constants for enums of parameters: <parameter>_<enum>, e.g. CIPHER_AES_128, DIRECTION_SEND, ...
41
- DESCRIPTION.each do |name, description|
42
- next unless description[:enum].is_a?(Array)
43
- const_set(:"#{name.to_s.upcase}_ENUM_VALUES", description[:enum])
44
- description[:enum].each do |enum|
40
+ SCHEMA['properties'].each do |name, description|
41
+ next unless description['enum'].is_a?(Array)
42
+ const_set(:"#{name.to_s.upcase}_ENUM_VALUES", description['enum'])
43
+ description['enum'].each do |enum|
45
44
  const_set("#{name.to_s.upcase}_#{enum.upcase.gsub(/[^A-Z0-9]/, '_')}", enum.freeze)
46
45
  end
47
46
  end