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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/BUGS.md +1 -1
- data/CHANGELOG.md +52 -22
- data/CONTRIBUTING.md +69 -148
- data/README.md +929 -668
- data/bin/ascli +5 -14
- data/bin/asession +1 -3
- data/examples/get_proto_file.rb +4 -3
- data/examples/proxy.pac +20 -20
- data/lib/aspera/agent/base.rb +11 -5
- data/lib/aspera/agent/connect.rb +30 -28
- data/lib/aspera/agent/{alpha.rb → desktop.rb} +35 -31
- data/lib/aspera/agent/direct.rb +141 -121
- data/lib/aspera/agent/httpgw.rb +22 -26
- data/lib/aspera/agent/node.rb +14 -11
- data/lib/aspera/agent/transferd.rb +30 -19
- data/lib/aspera/api/alee.rb +1 -1
- data/lib/aspera/api/aoc.rb +6 -6
- data/lib/aspera/api/cos_node.rb +2 -2
- data/lib/aspera/api/httpgw.rb +7 -3
- data/lib/aspera/api/node.rb +10 -8
- data/lib/aspera/ascmd.rb +3 -3
- data/lib/aspera/ascp/installation.rb +53 -72
- data/lib/aspera/ascp/management.rb +1 -1
- data/lib/aspera/assert.rb +11 -2
- data/lib/aspera/cli/error.rb +2 -2
- data/lib/aspera/cli/extended_value.rb +46 -21
- data/lib/aspera/cli/formatter.rb +55 -48
- data/lib/aspera/cli/hints.rb +1 -1
- data/lib/aspera/cli/info.rb +1 -0
- data/lib/aspera/cli/main.rb +192 -170
- data/lib/aspera/cli/manager.rb +18 -18
- data/lib/aspera/cli/plugin.rb +23 -20
- data/lib/aspera/cli/plugin_factory.rb +1 -1
- data/lib/aspera/cli/plugins/alee.rb +1 -1
- data/lib/aspera/cli/plugins/aoc.rb +247 -159
- data/lib/aspera/cli/plugins/ats.rb +19 -17
- data/lib/aspera/cli/plugins/config.rb +76 -113
- data/lib/aspera/cli/plugins/console.rb +5 -3
- data/lib/aspera/cli/plugins/faspex.rb +39 -35
- data/lib/aspera/cli/plugins/faspex5.rb +111 -84
- data/lib/aspera/cli/plugins/faspio.rb +13 -1
- data/lib/aspera/cli/plugins/httpgw.rb +13 -1
- data/lib/aspera/cli/plugins/node.rb +312 -182
- data/lib/aspera/cli/plugins/orchestrator.rb +34 -40
- data/lib/aspera/cli/plugins/preview.rb +3 -3
- data/lib/aspera/cli/plugins/server.rb +6 -6
- data/lib/aspera/cli/plugins/shares.rb +5 -5
- data/lib/aspera/cli/sync_actions.rb +19 -18
- data/lib/aspera/cli/transfer_agent.rb +5 -5
- data/lib/aspera/cli/transfer_progress.rb +2 -2
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/command_line_builder.rb +116 -95
- data/lib/aspera/coverage.rb +8 -5
- data/lib/aspera/environment.rb +26 -17
- data/lib/aspera/faspex_gw.rb +14 -14
- data/lib/aspera/faspex_postproc.rb +10 -11
- data/lib/aspera/hash_ext.rb +4 -14
- data/lib/aspera/json_rpc.rb +1 -1
- data/lib/aspera/keychain/encrypted_hash.rb +47 -34
- data/lib/aspera/keychain/factory.rb +41 -0
- data/lib/aspera/keychain/hashicorp_vault.rb +71 -0
- data/lib/aspera/keychain/macos_security.rb +19 -11
- data/lib/aspera/log.rb +28 -34
- data/lib/aspera/nagios.rb +6 -6
- data/lib/aspera/node_simulator.rb +8 -8
- data/lib/aspera/oauth/base.rb +14 -7
- data/lib/aspera/oauth/factory.rb +5 -6
- data/lib/aspera/oauth/url_json.rb +6 -6
- data/lib/aspera/persistency_action_once.rb +6 -4
- data/lib/aspera/persistency_folder.rb +2 -2
- data/lib/aspera/preview/generator.rb +13 -10
- data/lib/aspera/preview/options.rb +16 -16
- data/lib/aspera/preview/terminal.rb +4 -4
- data/lib/aspera/preview/utils.rb +15 -17
- data/lib/aspera/products/connect.rb +35 -1
- data/lib/aspera/products/{alpha.rb → desktop.rb} +3 -3
- data/lib/aspera/products/transferd.rb +9 -2
- data/lib/aspera/proxy_auto_config.rb +2 -2
- data/lib/aspera/rest.rb +56 -47
- data/lib/aspera/rest_errors_aspera.rb +1 -1
- data/lib/aspera/secret_hider.rb +12 -5
- data/lib/aspera/ssh.rb +4 -4
- data/lib/aspera/temp_file_manager.rb +5 -4
- data/lib/aspera/transfer/convert.rb +29 -0
- data/lib/aspera/transfer/error_info.rb +66 -66
- data/lib/aspera/transfer/parameters.rb +13 -68
- data/lib/aspera/transfer/spec.rb +5 -6
- data/lib/aspera/transfer/spec.schema.yaml +753 -0
- data/lib/aspera/transfer/spec_doc.rb +62 -0
- data/lib/aspera/transfer/sync.rb +23 -72
- data/lib/aspera/transfer/sync_instance.schema.yaml +13 -0
- data/lib/aspera/transfer/sync_session.schema.yaml +79 -0
- data/lib/aspera/transfer/uri.rb +6 -6
- data/lib/aspera/uri_reader.rb +18 -1
- data/lib/aspera/web_auth.rb +1 -1
- data/lib/aspera/web_server_simple.rb +53 -44
- data.tar.gz.sig +0 -0
- metadata +28 -165
- metadata.gz.sig +0 -0
- data/examples/build_exec +0 -74
- data/examples/build_exec_rubyc +0 -40
- data/examples/build_package.sh +0 -28
- data/lib/aspera/transfer/spec.yaml +0 -718
data/lib/aspera/secret_hider.rb
CHANGED
@@ -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 =
|
19
|
-
ALL_SECRETS2 =
|
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
|
28
|
-
Net::SSH::KnownHosts::SUPPORTED_TYPE.reject!
|
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,
|
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,
|
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(
|
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
|
-
|
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 => {
|
14
|
-
2 => {
|
15
|
-
3 => {
|
16
|
-
4 => {
|
17
|
-
5 => {
|
18
|
-
6 => {
|
19
|
-
7 => {
|
20
|
-
8 => {
|
21
|
-
9 => {
|
22
|
-
10 => {
|
23
|
-
11 => {
|
24
|
-
12 => {
|
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 => {
|
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 => {
|
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 => {
|
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 => {
|
33
|
-
17 => {
|
34
|
-
18 => {
|
35
|
-
19 => {
|
36
|
-
20 => {
|
37
|
-
21 => {
|
38
|
-
22 => {
|
39
|
-
23 => {
|
40
|
-
24 => {
|
41
|
-
25 => {
|
42
|
-
26 => {
|
43
|
-
27 => {
|
44
|
-
28 => {
|
45
|
-
29 => {
|
46
|
-
30 => {
|
47
|
-
31 => {
|
48
|
-
32 => {
|
49
|
-
33 => {
|
50
|
-
34 => {
|
51
|
-
35 => {
|
52
|
-
36 => {
|
53
|
-
37 => {
|
54
|
-
38 => {
|
55
|
-
39 => {
|
56
|
-
40 => {
|
57
|
-
41 => {
|
58
|
-
42 => {
|
59
|
-
43 => {
|
60
|
-
44 => {
|
61
|
-
45 => {
|
62
|
-
46 => {
|
63
|
-
47 => {
|
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 => {
|
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 => {
|
68
|
-
50 => {
|
69
|
-
51 => {
|
70
|
-
52 => {
|
71
|
-
53 => {
|
72
|
-
54 => {
|
73
|
-
55 => {
|
74
|
-
56 => {
|
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 => {
|
77
|
-
58 => {
|
78
|
-
59 => {
|
79
|
-
60 => {
|
80
|
-
61 => {
|
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 => {
|
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 => {
|
85
|
-
64 => {
|
86
|
-
65 => {
|
87
|
-
66 => {
|
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
|
-
|
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('/', '\\')
|
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
|
-
# @
|
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
|
-
# @
|
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::
|
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.
|
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)}
|
data/lib/aspera/transfer/spec.rb
CHANGED
@@ -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
|
-
|
38
|
+
SCHEMA = CommandLineBuilder.read_schema(__FILE__)
|
40
39
|
# define constants for enums of parameters: <parameter>_<enum>, e.g. CIPHER_AES_128, DIRECTION_SEND, ...
|
41
|
-
|
42
|
-
next unless description[
|
43
|
-
const_set(:"#{name.to_s.upcase}_ENUM_VALUES", description[
|
44
|
-
description[
|
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
|