aspera-cli 4.24.1 → 4.24.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +15 -2
- data/README.md +745 -436
- data/bin/ascli +20 -1
- data/bin/asession +23 -27
- data/lib/aspera/agent/base.rb +10 -21
- data/lib/aspera/agent/connect.rb +2 -3
- data/lib/aspera/agent/desktop.rb +2 -2
- data/lib/aspera/agent/direct.rb +49 -32
- data/lib/aspera/agent/factory.rb +31 -0
- data/lib/aspera/api/aoc.rb +79 -49
- data/lib/aspera/api/faspex.rb +212 -0
- data/lib/aspera/api/node.rb +99 -84
- data/lib/aspera/ascp/installation.rb +22 -21
- data/lib/aspera/ascp/management.rb +119 -23
- data/lib/aspera/assert.rb +14 -8
- data/lib/aspera/cli/extended_value.rb +15 -15
- data/lib/aspera/cli/formatter.rb +7 -5
- data/lib/aspera/cli/hints.rb +8 -0
- data/lib/aspera/cli/info.rb +4 -4
- data/lib/aspera/cli/main.rb +55 -70
- data/lib/aspera/cli/manager.rb +7 -4
- data/lib/aspera/cli/plugins/alee.rb +2 -1
- data/lib/aspera/cli/plugins/aoc.rb +110 -186
- data/lib/aspera/cli/plugins/ats.rb +4 -4
- data/lib/aspera/cli/plugins/base.rb +335 -0
- data/lib/aspera/cli/plugins/basic_auth.rb +45 -0
- data/lib/aspera/cli/plugins/config.rb +249 -220
- data/lib/aspera/cli/plugins/console.rb +15 -15
- data/lib/aspera/cli/plugins/cos.rb +2 -2
- data/lib/aspera/cli/plugins/factory.rb +78 -0
- data/lib/aspera/cli/plugins/faspex.rb +17 -20
- data/lib/aspera/cli/plugins/faspex5.rb +79 -193
- data/lib/aspera/cli/plugins/faspio.rb +14 -13
- data/lib/aspera/cli/plugins/httpgw.rb +13 -12
- data/lib/aspera/cli/plugins/node.rb +34 -32
- data/lib/aspera/cli/plugins/oauth.rb +48 -0
- data/lib/aspera/cli/plugins/orchestrator.rb +15 -13
- data/lib/aspera/cli/plugins/preview.rb +4 -4
- data/lib/aspera/cli/plugins/server.rb +15 -13
- data/lib/aspera/cli/plugins/shares.rb +18 -15
- data/lib/aspera/cli/sync_actions.rb +1 -1
- data/lib/aspera/cli/transfer_agent.rb +24 -20
- data/lib/aspera/cli/transfer_progress.rb +6 -6
- data/lib/aspera/cli/version.rb +3 -3
- data/lib/aspera/cli/wizard.rb +65 -53
- data/lib/aspera/colors.rb +6 -0
- data/lib/aspera/command_line_builder.rb +45 -50
- data/lib/aspera/command_line_converter.rb +2 -1
- data/lib/aspera/coverage.rb +1 -1
- data/lib/aspera/data_repository.rb +1 -1
- data/lib/aspera/environment.rb +10 -7
- data/lib/aspera/faspex_gw.rb +6 -4
- data/lib/aspera/faspex_postproc.rb +1 -1
- data/lib/aspera/keychain/macos_security.rb +1 -1
- data/lib/aspera/log.rb +37 -9
- data/lib/aspera/nagios.rb +1 -1
- data/lib/aspera/oauth/base.rb +17 -10
- data/lib/aspera/oauth/factory.rb +8 -8
- data/lib/aspera/oauth/web.rb +2 -2
- data/lib/aspera/products/connect.rb +4 -3
- data/lib/aspera/products/desktop.rb +1 -4
- data/lib/aspera/products/other.rb +9 -1
- data/lib/aspera/products/transferd.rb +0 -1
- data/lib/aspera/rest.rb +126 -83
- data/lib/aspera/ssh.rb +3 -3
- data/lib/aspera/sync/args.schema.yaml +46 -3
- data/lib/aspera/sync/conf.schema.yaml +130 -94
- data/lib/aspera/sync/operations.rb +16 -16
- data/lib/aspera/temp_file_manager.rb +17 -5
- data/lib/aspera/transfer/error.rb +16 -7
- data/lib/aspera/transfer/parameters.rb +34 -20
- data/lib/aspera/transfer/resumer.rb +74 -0
- data/lib/aspera/transfer/spec.rb +4 -3
- data/lib/aspera/transfer/spec.schema.yaml +132 -51
- data/lib/aspera/transfer/spec_doc.rb +41 -35
- data/lib/aspera/uri_reader.rb +1 -1
- data/lib/aspera/web_auth.rb +6 -6
- data.tar.gz.sig +0 -0
- metadata +9 -7
- metadata.gz.sig +0 -0
- data/lib/aspera/cli/basic_auth_plugin.rb +0 -43
- data/lib/aspera/cli/plugin.rb +0 -333
- data/lib/aspera/cli/plugin_factory.rb +0 -81
- data/lib/aspera/resumer.rb +0 -77
- data/lib/aspera/transfer/error_info.rb +0 -91
|
@@ -18,16 +18,16 @@ module Aspera
|
|
|
18
18
|
module Sync
|
|
19
19
|
# builds command line arg for async and execute it
|
|
20
20
|
module Operations
|
|
21
|
-
#
|
|
21
|
+
# Sync direction
|
|
22
22
|
DIRECTIONS = %i[push pull bidi].freeze
|
|
23
|
-
#
|
|
24
|
-
DEFAULT_DIRECTION =
|
|
23
|
+
# Default direction for sync
|
|
24
|
+
DEFAULT_DIRECTION = DIRECTIONS.first
|
|
25
25
|
|
|
26
26
|
class << self
|
|
27
27
|
# Set `remote_dir` in sync parameters based on transfer spec
|
|
28
|
-
# @param params
|
|
29
|
-
# @param remote_dir_key [String]
|
|
30
|
-
# @param transfer_spec
|
|
28
|
+
# @param params [Hash] Sync parameters, in `conf` or `args` format.
|
|
29
|
+
# @param remote_dir_key [String] Key to update in above hash
|
|
30
|
+
# @param transfer_spec [Hash] Transfer spec
|
|
31
31
|
def update_remote_dir(params, remote_dir_key, transfer_spec)
|
|
32
32
|
if transfer_spec.dig(*%w[tags aspera node file_id])
|
|
33
33
|
# in AoC, use gen4
|
|
@@ -126,21 +126,21 @@ module Aspera
|
|
|
126
126
|
transfer_spec = yield(direction_sym(session), session['local_dir'], session['remote_dir'])
|
|
127
127
|
Log.dump(:auth_ts, transfer_spec)
|
|
128
128
|
transfer_spec.deep_merge!(opt_ts) unless opt_ts.nil?
|
|
129
|
-
tspec_to_sync_info(transfer_spec, session,
|
|
129
|
+
tspec_to_sync_info(transfer_spec, session, ARGS_SESSION_SCHEMA)
|
|
130
130
|
session['private_key_paths'] = Ascp::Installation.instance.aspera_token_ssh_key_paths(:rsa) if transfer_spec.key?('token')
|
|
131
131
|
update_remote_dir(session, 'remote_dir', transfer_spec)
|
|
132
132
|
end
|
|
133
133
|
end
|
|
134
134
|
if params.key?('instance')
|
|
135
135
|
Aspera.assert_type(params['instance'], Hash)
|
|
136
|
-
instance_builder = CommandLineBuilder.new(params['instance'],
|
|
136
|
+
instance_builder = CommandLineBuilder.new(params['instance'], ARGS_INSTANCE_SCHEMA, CommandLineConverter)
|
|
137
137
|
instance_builder.process_params
|
|
138
138
|
instance_builder.add_env_args(env_args)
|
|
139
139
|
end
|
|
140
140
|
params['sessions'].each do |session_params|
|
|
141
141
|
Aspera.assert_type(session_params, Hash)
|
|
142
142
|
Aspera.assert(session_params.key?('name')){'session must contain at least: name'}
|
|
143
|
-
session_builder = CommandLineBuilder.new(session_params,
|
|
143
|
+
session_builder = CommandLineBuilder.new(session_params, ARGS_SESSION_SCHEMA, CommandLineConverter)
|
|
144
144
|
session_builder.process_params
|
|
145
145
|
session_builder.add_env_args(env_args)
|
|
146
146
|
end
|
|
@@ -277,20 +277,20 @@ module Aspera
|
|
|
277
277
|
end
|
|
278
278
|
# Private stuff:
|
|
279
279
|
# Read JSON schema and mapping to command line options
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
280
|
+
ARGS_INSTANCE_SCHEMA = CommandLineBuilder.read_schema(__FILE__, 'args')
|
|
281
|
+
ARGS_SESSION_SCHEMA = ARGS_INSTANCE_SCHEMA['properties']['sessions']['items']
|
|
282
|
+
ARGS_INSTANCE_SCHEMA['properties'].delete('sessions')
|
|
283
283
|
CONF_SCHEMA = CommandLineBuilder.read_schema(__FILE__, 'conf')
|
|
284
|
-
CommandLineBuilder.
|
|
285
|
-
CommandLineBuilder.
|
|
286
|
-
CommandLineBuilder.
|
|
284
|
+
CommandLineBuilder.validate_schema(ARGS_INSTANCE_SCHEMA)
|
|
285
|
+
CommandLineBuilder.validate_schema(ARGS_SESSION_SCHEMA)
|
|
286
|
+
CommandLineBuilder.validate_schema(CONF_SCHEMA)
|
|
287
287
|
CMDLINE_PARAMS_KEYS = %w[instance sessions].freeze
|
|
288
288
|
ASYNC_ADMIN_EXECUTABLE = 'asyncadmin'
|
|
289
289
|
PRIVATE_FOLDER = '.private-asp'
|
|
290
290
|
ASYNC_DB = 'snap.db'
|
|
291
291
|
PARAM_KEYS = %w[local sessions].freeze
|
|
292
292
|
|
|
293
|
-
private_constant :
|
|
293
|
+
private_constant :ARGS_INSTANCE_SCHEMA, :ARGS_SESSION_SCHEMA, :CMDLINE_PARAMS_KEYS, :ASYNC_ADMIN_EXECUTABLE, :PRIVATE_FOLDER, :ASYNC_DB, :PARAM_KEYS
|
|
294
294
|
end
|
|
295
295
|
end
|
|
296
296
|
end
|
|
@@ -17,17 +17,29 @@ module Aspera
|
|
|
17
17
|
private_constant :SEC_IN_DAY, :FILE_LIST_AGE_MAX_SEC
|
|
18
18
|
|
|
19
19
|
attr_accessor :cleanup_on_exit
|
|
20
|
+
attr_reader :global_temp
|
|
20
21
|
|
|
21
22
|
def initialize
|
|
22
23
|
@created_files = []
|
|
23
24
|
@cleanup_on_exit = true
|
|
25
|
+
@global_temp = Etc.systmpdir
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def global_temp=(value)
|
|
29
|
+
@global_temp = case value
|
|
30
|
+
when '@env' then Dir.tmpdir
|
|
31
|
+
when '@sys' then Etc.systmpdir
|
|
32
|
+
else value
|
|
33
|
+
end
|
|
24
34
|
end
|
|
25
35
|
|
|
26
36
|
def delete_file(filepath)
|
|
27
37
|
File.delete(filepath) if @cleanup_on_exit
|
|
38
|
+
rescue => e
|
|
39
|
+
Log.log.warn{"Problem deleting file: #{filepath}: #{e.message}"}
|
|
28
40
|
end
|
|
29
41
|
|
|
30
|
-
#
|
|
42
|
+
# Call this on process exit
|
|
31
43
|
def cleanup
|
|
32
44
|
@created_files.each do |filepath|
|
|
33
45
|
delete_file(filepath) if File.file?(filepath)
|
|
@@ -35,7 +47,7 @@ module Aspera
|
|
|
35
47
|
@created_files = []
|
|
36
48
|
end
|
|
37
49
|
|
|
38
|
-
#
|
|
50
|
+
# Ensure that provided folder exists, or create it, generate a unique filename
|
|
39
51
|
# @return path to that unique file
|
|
40
52
|
def new_file_path_in_folder(temp_folder, prefix: nil, suffix: nil)
|
|
41
53
|
FileUtils.mkdir_p(temp_folder)
|
|
@@ -44,7 +56,7 @@ module Aspera
|
|
|
44
56
|
new_file
|
|
45
57
|
end
|
|
46
58
|
|
|
47
|
-
#
|
|
59
|
+
# Same as above but in global temp folder, with user's name
|
|
48
60
|
def new_file_path_global(prefix = nil, suffix: nil)
|
|
49
61
|
username =
|
|
50
62
|
begin
|
|
@@ -53,11 +65,11 @@ module Aspera
|
|
|
53
65
|
'unknown_user'
|
|
54
66
|
end
|
|
55
67
|
prefix = [prefix, username].compact.join('-')
|
|
56
|
-
new_file_path_in_folder(
|
|
68
|
+
new_file_path_in_folder(@global_temp, prefix: prefix, suffix: suffix)
|
|
57
69
|
end
|
|
58
70
|
|
|
71
|
+
# Garbage collect undeleted files
|
|
59
72
|
def cleanup_expired(temp_folder)
|
|
60
|
-
# garbage collect undeleted files
|
|
61
73
|
Dir.entries(temp_folder).each do |name|
|
|
62
74
|
file_path = File.join(temp_folder, name)
|
|
63
75
|
age_sec = (Time.now - File.stat(file_path).mtime).to_i
|
|
@@ -1,24 +1,33 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'aspera/
|
|
3
|
+
require 'aspera/ascp/management'
|
|
4
4
|
|
|
5
5
|
module Aspera
|
|
6
6
|
module Transfer
|
|
7
|
-
#
|
|
7
|
+
# Error raised if transfer fails
|
|
8
8
|
class Error < StandardError
|
|
9
|
+
# Error code like on management port
|
|
9
10
|
attr_reader :err_code
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
# @param description [String] `Description` on management port
|
|
13
|
+
# @param code [Integer] `Description` on management port, use zero if unknown
|
|
14
|
+
def initialize(description, code: nil)
|
|
15
|
+
super(description)
|
|
16
|
+
@err_code = code.to_i
|
|
14
17
|
end
|
|
15
18
|
|
|
19
|
+
# @return [Hash] Information on that error
|
|
16
20
|
def info
|
|
17
|
-
r =
|
|
21
|
+
r = Ascp::Management::ERRORS[@err_code] || Ascp::Management::ERRORS[0]
|
|
18
22
|
return r.merge({i: @err_code})
|
|
19
23
|
end
|
|
20
24
|
|
|
21
|
-
|
|
25
|
+
# Is that transfer error retryable ?
|
|
26
|
+
# @param message [String, nil] Optional actual message on management port
|
|
27
|
+
def retryable?
|
|
28
|
+
return false if @err_code.eql?(14) && message.eql?('Target address not available')
|
|
29
|
+
info[:r]
|
|
30
|
+
end
|
|
22
31
|
end
|
|
23
32
|
end
|
|
24
33
|
end
|
|
@@ -18,11 +18,8 @@ require 'openssl'
|
|
|
18
18
|
|
|
19
19
|
module Aspera
|
|
20
20
|
module Transfer
|
|
21
|
-
#
|
|
21
|
+
# Translate transfer specification to `ascp` parameter list
|
|
22
22
|
class Parameters
|
|
23
|
-
# `ascp` options to provide a file list
|
|
24
|
-
FILE_LIST_OPTIONS = ['--file-list', '--file-pair-list'].freeze
|
|
25
|
-
private_constant :FILE_LIST_OPTIONS
|
|
26
23
|
HTTP_FALLBACK_ACTIVATION_VALUES = ['1', 1, true, 'force'].freeze
|
|
27
24
|
|
|
28
25
|
class << self
|
|
@@ -30,7 +27,6 @@ module Aspera
|
|
|
30
27
|
def file_list_folder=(value)
|
|
31
28
|
@file_list_folder = value
|
|
32
29
|
return if @file_list_folder.nil?
|
|
33
|
-
|
|
34
30
|
FileUtils.mkdir_p(@file_list_folder)
|
|
35
31
|
TempFileManager.instance.cleanup_expired(@file_list_folder)
|
|
36
32
|
end
|
|
@@ -42,14 +38,19 @@ module Aspera
|
|
|
42
38
|
@file_list_folder ||= TempFileManager.instance.new_file_path_global('asession_filelists')
|
|
43
39
|
end
|
|
44
40
|
|
|
45
|
-
#
|
|
41
|
+
# File list is provided directly with ascp arguments
|
|
46
42
|
# @columns ascp_args [Array,NilClass] ascp arguments
|
|
47
43
|
def ascp_args_file_list?(ascp_args)
|
|
48
44
|
ascp_args&.any?{ |i| FILE_LIST_OPTIONS.include?(i)}
|
|
49
45
|
end
|
|
50
46
|
end
|
|
51
47
|
|
|
52
|
-
# @
|
|
48
|
+
# @param job_spec [Hash] Transfer spec
|
|
49
|
+
# @param ascp_args [Array] Other ascp args
|
|
50
|
+
# @param quiet [Bool] Remove ascp output
|
|
51
|
+
# @param trusted_certs [Array] Trusted certificates
|
|
52
|
+
# @param client_ssh_key [Symbol] :rsa or :dsa
|
|
53
|
+
# @param check_ignore_cb [Proc] Callback
|
|
53
54
|
def initialize(
|
|
54
55
|
job_spec,
|
|
55
56
|
ascp_args: nil,
|
|
@@ -60,17 +61,17 @@ module Aspera
|
|
|
60
61
|
check_ignore_cb: nil
|
|
61
62
|
)
|
|
62
63
|
@job_spec = job_spec
|
|
64
|
+
Aspera.assert_type(@job_spec, Hash)
|
|
63
65
|
@ascp_args = ascp_args.nil? ? [] : ascp_args
|
|
66
|
+
Aspera.assert_type(@ascp_args, Array){'ascp_args'}
|
|
67
|
+
Aspera.assert(@ascp_args.all?(String)){'all ascp arguments must be String'}
|
|
64
68
|
@wss = wss
|
|
65
69
|
@quiet = quiet
|
|
66
70
|
@trusted_certs = trusted_certs.nil? ? [] : trusted_certs
|
|
67
|
-
@client_ssh_key = client_ssh_key.nil? ? :rsa : client_ssh_key.to_sym
|
|
68
|
-
@check_ignore_cb = check_ignore_cb
|
|
69
|
-
Aspera.assert_type(@job_spec, Hash)
|
|
70
|
-
Aspera.assert_type(@ascp_args, Array){'ascp_args'}
|
|
71
|
-
Aspera.assert(@ascp_args.all?(String)){'all ascp arguments must be String'}
|
|
72
71
|
Aspera.assert_type(@trusted_certs, Array){'trusted_certs'}
|
|
72
|
+
@client_ssh_key = client_ssh_key.nil? ? :rsa : client_ssh_key.to_sym
|
|
73
73
|
Aspera.assert_values(@client_ssh_key, Ascp::Installation::CLIENT_SSH_KEY_OPTIONS)
|
|
74
|
+
@check_ignore_cb = check_ignore_cb
|
|
74
75
|
@builder = CommandLineBuilder.new(@job_spec, Spec::SCHEMA, CommandLineConverter)
|
|
75
76
|
end
|
|
76
77
|
|
|
@@ -109,7 +110,7 @@ module Aspera
|
|
|
109
110
|
Log.log.debug{"#{file_list_option}=\n#{File.read(file_list_file)}".red}
|
|
110
111
|
end
|
|
111
112
|
end
|
|
112
|
-
@builder.add_command_line_options(
|
|
113
|
+
@builder.add_command_line_options("#{file_list_option}=#{file_list_file}") unless file_list_option.nil?
|
|
113
114
|
end
|
|
114
115
|
|
|
115
116
|
# @return the list of certificates (option `-i`) to use when token/ssh or wss are used
|
|
@@ -118,7 +119,7 @@ module Aspera
|
|
|
118
119
|
# use web socket secure for session ?
|
|
119
120
|
if @builder.read_param('wss_enabled') && (@wss || !@job_spec.key?('fasp_port'))
|
|
120
121
|
# by default use web socket session if available, unless removed by user
|
|
121
|
-
@builder.add_command_line_options(
|
|
122
|
+
@builder.add_command_line_options('--ws-connect')
|
|
122
123
|
# TODO: option to give order ssh,ws (legacy http is implied by ssh)
|
|
123
124
|
# This will need to be cleaned up in aspera core
|
|
124
125
|
@job_spec['ssh_port'] = @builder.read_param('wss_port')
|
|
@@ -148,7 +149,7 @@ module Aspera
|
|
|
148
149
|
return certificates_to_use
|
|
149
150
|
end
|
|
150
151
|
|
|
151
|
-
#
|
|
152
|
+
# Translate transfer spec to env vars and command line arguments for `ascp`
|
|
152
153
|
def ascp_args
|
|
153
154
|
env_args = {
|
|
154
155
|
args: [],
|
|
@@ -156,18 +157,27 @@ module Aspera
|
|
|
156
157
|
name: :ascp
|
|
157
158
|
}
|
|
158
159
|
|
|
159
|
-
#
|
|
160
|
+
# Special cases
|
|
160
161
|
@job_spec.delete('source_root') if @job_spec.key?('source_root') && @job_spec['source_root'].empty?
|
|
161
162
|
|
|
162
|
-
#
|
|
163
|
+
# Notify multi-session was already used, anyway it was deleted by agent direct
|
|
163
164
|
Aspera.assert(!@builder.read_param('multi_session'))
|
|
164
165
|
|
|
165
|
-
#
|
|
166
|
+
# Add ssh or wss certificates
|
|
166
167
|
# (reverse, to keep order, as we unshift)
|
|
167
168
|
remote_certificates&.reverse_each do |cert|
|
|
168
169
|
env_args[:args].unshift('-i', cert)
|
|
169
170
|
end
|
|
170
171
|
|
|
172
|
+
case (delete_source = @builder.read_param('delete_source'))
|
|
173
|
+
when true
|
|
174
|
+
DELETE_EQUIV.each{ |i| @job_spec[i] = true}
|
|
175
|
+
when false
|
|
176
|
+
DELETE_EQUIV.each{ |i| @job_spec.delete(i)}
|
|
177
|
+
when nil
|
|
178
|
+
else Aspera.error_unexpected_value(delete_source){'delete_source'}
|
|
179
|
+
end
|
|
180
|
+
|
|
171
181
|
# process parameters as specified in table
|
|
172
182
|
@builder.process_params
|
|
173
183
|
|
|
@@ -180,7 +190,7 @@ module Aspera
|
|
|
180
190
|
base64_destination = true
|
|
181
191
|
end
|
|
182
192
|
# destination will be base64 encoded, put this before source path arguments
|
|
183
|
-
@builder.add_command_line_options(
|
|
193
|
+
@builder.add_command_line_options('--dest64') if base64_destination
|
|
184
194
|
# optional arguments, at the end to override previous ones (to allow override)
|
|
185
195
|
@builder.add_command_line_options(@ascp_args)
|
|
186
196
|
# get list of source files to transfer and build arg for ascp
|
|
@@ -190,7 +200,7 @@ module Aspera
|
|
|
190
200
|
# ascp4 does not support base64 encoding of destination
|
|
191
201
|
destination_folder = Base64.strict_encode64(destination_folder) if base64_destination
|
|
192
202
|
# destination MUST be last command line argument to ascp
|
|
193
|
-
@builder.add_command_line_options(
|
|
203
|
+
@builder.add_command_line_options(destination_folder)
|
|
194
204
|
@builder.add_env_args(env_args)
|
|
195
205
|
env_args[:args].unshift('-q') if @quiet
|
|
196
206
|
# add fallback cert and key as arguments if needed
|
|
@@ -203,6 +213,10 @@ module Aspera
|
|
|
203
213
|
Log.log.debug{"ascp args: #{env_args}"}
|
|
204
214
|
return env_args
|
|
205
215
|
end
|
|
216
|
+
DELETE_EQUIV = %w[remove_after_transfer remove_empty_directories remove_empty_source_directory]
|
|
217
|
+
# `ascp` options to provide a file list
|
|
218
|
+
FILE_LIST_OPTIONS = ['--file-list', '--file-pair-list'].freeze
|
|
219
|
+
private_constant :DELETE_EQUIV, :FILE_LIST_OPTIONS
|
|
206
220
|
end
|
|
207
221
|
end
|
|
208
222
|
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'singleton'
|
|
4
|
+
require 'aspera/log'
|
|
5
|
+
require 'aspera/assert'
|
|
6
|
+
require 'aspera/transfer/error'
|
|
7
|
+
|
|
8
|
+
module Aspera
|
|
9
|
+
module Transfer
|
|
10
|
+
# Implements a simple resume policy
|
|
11
|
+
class Resumer
|
|
12
|
+
# @param iter_max [Integer] Maximum number of executions
|
|
13
|
+
# @param sleep_initial [Integer] Initial wait to re-execute
|
|
14
|
+
# @param sleep_factor [Integer] Multiplier
|
|
15
|
+
# @param sleep_max. [Integer] Max iterations
|
|
16
|
+
def initialize(
|
|
17
|
+
iter_max: 7,
|
|
18
|
+
sleep_initial: 2,
|
|
19
|
+
sleep_factor: 2,
|
|
20
|
+
sleep_max: 60
|
|
21
|
+
)
|
|
22
|
+
Aspera.assert_type(iter_max, Integer){k}
|
|
23
|
+
@iter_max = iter_max
|
|
24
|
+
Aspera.assert_type(sleep_initial, Integer){k}
|
|
25
|
+
@sleep_initial = sleep_initial
|
|
26
|
+
Aspera.assert_type(sleep_factor, Integer){k}
|
|
27
|
+
@sleep_factor = sleep_factor
|
|
28
|
+
Aspera.assert_type(sleep_max, Integer){k}
|
|
29
|
+
@sleep_max = sleep_max
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Calls block a number of times (resumes) until success or limit reached
|
|
33
|
+
# This is re-entrant, one resumer can handle multiple transfers in //
|
|
34
|
+
#
|
|
35
|
+
# @param block [Proc]
|
|
36
|
+
def execute_with_resume
|
|
37
|
+
Aspera.assert(block_given?)
|
|
38
|
+
# maximum of retry
|
|
39
|
+
remaining_resumes = @iter_max
|
|
40
|
+
sleep_seconds = @sleep_initial
|
|
41
|
+
Log.log.debug{"retries=#{remaining_resumes}"}
|
|
42
|
+
# try to send the file until ascp is successful
|
|
43
|
+
loop do
|
|
44
|
+
Log.log.debug('Transfer session starting')
|
|
45
|
+
begin
|
|
46
|
+
# Call provided block: execute transfer
|
|
47
|
+
yield
|
|
48
|
+
# Exit retry loop if success
|
|
49
|
+
break
|
|
50
|
+
rescue Error => e
|
|
51
|
+
Log.log.warn{"A transfer error occurred during transfer: #{e.message}"}
|
|
52
|
+
Log.log.debug{"Retryable ? #{e.retryable?}"}
|
|
53
|
+
# do not retry non-retryable
|
|
54
|
+
raise unless e.retryable?
|
|
55
|
+
# exit if we exceed the max number of retry
|
|
56
|
+
raise Error, "Maximum number of retry reached: #{@iter_max}" if remaining_resumes <= 0
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# take this retry in account
|
|
60
|
+
remaining_resumes -= 1
|
|
61
|
+
Log.log.warn{"Resuming in #{sleep_seconds} seconds (retry left:#{remaining_resumes})"}
|
|
62
|
+
|
|
63
|
+
# wait a bit before retrying, maybe network condition will be better
|
|
64
|
+
sleep(sleep_seconds)
|
|
65
|
+
|
|
66
|
+
# increase retry period
|
|
67
|
+
sleep_seconds *= @sleep_factor
|
|
68
|
+
# cap value
|
|
69
|
+
sleep_seconds = @sleep_max if sleep_seconds > @sleep_max
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
data/lib/aspera/transfer/spec.rb
CHANGED
|
@@ -5,7 +5,8 @@ require 'aspera/assert'
|
|
|
5
5
|
|
|
6
6
|
module Aspera
|
|
7
7
|
module Transfer
|
|
8
|
-
#
|
|
8
|
+
# Parameters for Transfer Spec
|
|
9
|
+
# Parameters are generated from JSON Schema.
|
|
9
10
|
class Spec
|
|
10
11
|
# default transfer username for access key based transfers
|
|
11
12
|
ACCESS_KEY_TRANSFER_USER = 'xfer'
|
|
@@ -44,12 +45,12 @@ module Aspera
|
|
|
44
45
|
end
|
|
45
46
|
|
|
46
47
|
def fix_transferd_resume_policy(transfer_spec)
|
|
47
|
-
# Fix
|
|
48
|
+
# Fix discrepancy in transfer spec
|
|
48
49
|
transfer_spec['resume_policy'] = POLICY_FIX[transfer_spec['resume_policy']] if transfer_spec.key?('resume_policy')
|
|
49
50
|
end
|
|
50
51
|
end
|
|
51
52
|
SCHEMA = CommandLineBuilder.read_schema(__FILE__, 'spec')
|
|
52
|
-
CommandLineBuilder.
|
|
53
|
+
CommandLineBuilder.validate_schema(SCHEMA, ascp: true)
|
|
53
54
|
# define constants for enums of parameters: <parameter>_<enum>, e.g. CIPHER_AES_128, DIRECTION_SEND, ...
|
|
54
55
|
SCHEMA['properties'].each do |name, description|
|
|
55
56
|
next unless description['enum'].is_a?(Array)
|